FreeCAD

Форк
0
/
fcinfo 
293 строки · 10.7 Кб
1
#!/usr/bin/python3
2

3
# -*- coding: utf8 -*-
4

5
# ***************************************************************************
6
# *                                                                         *
7
# *   Copyright (c) 2015 Yorik van Havre <yorik@uncreated.net>              *
8
# *                                                                         *
9
# *   This program is free software; you can redistribute it and/or modify  *
10
# *   it under the terms of the GNU Lesser General Public License (LGPL)    *
11
# *   as published by the Free Software Foundation; either version 2 of     *
12
# *   the License, or (at your option) any later version.                   *
13
# *   for detail see the LICENCE text file.                                 *
14
# *                                                                         *
15
# *   This program is distributed in the hope that it will be useful,       *
16
# *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
17
# *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
18
# *   GNU Library General Public License for more details.                  *
19
# *                                                                         *
20
# *   You should have received a copy of the GNU Library General Public     *
21
# *   License along with this program; if not, write to the Free Software   *
22
# *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
23
# *   USA                                                                   *
24
# *                                                                         *
25
# ***************************************************************************
26

27
__title__ = "FreeCAD File info utility"
28
__author__ = "Yorik van Havre"
29
__url__ = ["https://www.freecad.org"]
30
__doc__ = """
31
This utility prints information about a given FreeCAD file (*.FCStd)
32
on screen, including document properties, number of included objects,
33
object sizes and properties and values. Its main use is to compare
34
two files and be able to see the differences in a text-based form.
35

36
If no option is used, fcinfo prints the document properties and a list
37
of properties of each object found in the given file.
38

39
Usage:
40

41
    fcinfo [options] myfile.FCStd
42

43
Options:
44

45
    -h, --help:      Prints this help text
46
    -s, --short:     Do not print object properties. Only one line
47
                     per object is printed, including its size and SHA1.
48
                     This is sufficient to see that an object has
49
                     changed, but not what exactly has changed.
50
    -vs --veryshort: Only prints the document info, not objects info.
51
                     This is sufficient to see if a file has changed, as
52
                     its SHA1 code and timestamp will show it. But won't
53
                     show details of what has changed.
54
    -g  --gui:       Adds visual properties too (if not using -s or -vs)
55

56
Git usage:
57

58
    This script can be used as a textconv tool for git diff by
59
    configuring your git folder as follows:
60

61
     1) add to .gitattributes (or ~/.gitattributes for user-wide):
62

63
         *.fcstd diff=fcinfo
64

65
     2) add to .git/config (or ~/.gitconfig for user-wide):
66

67
         [diff "fcinfo"]
68
             textconv = /path/to/fcinfo
69

70
    With this, when committing a .FCStd file with Git,
71
    'git diff' will show you the difference between the two
72
    texts obtained by fcinfo
73
"""
74

75

76
import sys
77
import zipfile
78
import xml.sax
79
import os
80
import hashlib
81
import re
82

83

84
class FreeCADFileHandler(xml.sax.ContentHandler):
85
    def __init__(self, zfile, short=0):  # short: 0=normal, 1=short, 2=veryshort
86

87
        xml.sax.ContentHandler.__init__(self)
88
        self.zfile = zfile
89
        self.obj = None
90
        self.prop = None
91
        self.count = "0"
92
        self.contents = {}
93
        self.short = short
94

95
    def startElement(self, tag, attributes):
96

97
        if tag == "Document":
98
            self.obj = tag
99
            self.contents = {}
100
            self.contents["ProgramVersion"] = attributes["ProgramVersion"]
101
            self.contents["FileVersion"] = attributes["FileVersion"]
102

103
        elif tag == "Object":
104
            if "name" in attributes:
105
                name = self.clean(attributes["name"])
106
                self.obj = name
107
                if "type" in attributes:
108
                    self.contents[name] = attributes["type"]
109

110
        elif tag == "ViewProvider":
111
            if "name" in attributes:
112
                self.obj = self.clean(attributes["name"])
113

114
        elif tag == "Part":
115
            if self.obj:
116
                r = self.zfile.read(attributes["file"])
117
                s = r.__sizeof__()
118
                if s < 1024:
119
                    s = str(s) + "B"
120
                elif s > 1048576:
121
                    s = str(s / 1048576) + "M"
122
                else:
123
                    s = str(s / 1024) + "K"
124
                s += " " + str(hashlib.sha1(r).hexdigest()[:12])
125
                self.contents[self.obj] += " (" + s + ")"
126

127
        elif tag == "Property":
128
            self.prop = None
129
            # skip "internal" properties, useless for a diff
130
            if attributes["name"] not in [
131
                "Symbol",
132
                "AttacherType",
133
                "MapMode",
134
                "MapPathParameter",
135
                "MapReversed",
136
                "AttachmentOffset",
137
                "SelectionStyle",
138
                "TightGrid",
139
                "GridSize",
140
                "GridSnap",
141
                "GridStyle",
142
                "Lighting",
143
                "Deviation",
144
                "AngularDeflection",
145
                "BoundingBox",
146
                "Selectable",
147
                "ShowGrid",
148
            ]:
149
                self.prop = attributes["name"]
150

151
        elif tag in ["String", "Uuid", "Float", "Integer", "Bool", "Link"]:
152
            if self.prop and ("value" in attributes):
153
                if self.obj == "Document":
154
                    self.contents[self.prop] = attributes["value"]
155
                elif self.short == 0:
156
                    if tag == "Float":
157
                        self.contents[self.obj + "00000000::" + self.prop] = str(
158
                            float(attributes["value"])
159
                        )
160
                    else:
161
                        self.contents[self.obj + "00000000::" + self.prop] = attributes["value"]
162

163
        elif tag in ["PropertyVector"]:
164
            if self.prop and self.obj and (self.short == 0):
165
                val = (
166
                    "("
167
                    + str(float(attributes["valueX"]))
168
                    + ","
169
                    + str(float(attributes["valueY"]))
170
                    + ","
171
                    + str(float(attributes["valueZ"]))
172
                    + ")"
173
                )
174
                self.contents[self.obj + "00000000::" + self.prop] = val
175

176
        elif tag in ["PropertyPlacement"]:
177
            if self.prop and self.obj and (self.short == 0):
178
                val = (
179
                    "("
180
                    + str(float(attributes["Px"]))
181
                    + ","
182
                    + str(float(attributes["Py"]))
183
                    + ","
184
                    + str(float(attributes["Pz"]))
185
                    + ")"
186
                )
187
                val += (
188
                    " ("
189
                    + str(round(float(attributes["Q0"]), 4))
190
                    + ","
191
                    + str(round(float(attributes["Q1"]), 4))
192
                    + ","
193
                )
194
                val += (
195
                    str(round(float(attributes["Q2"]), 4))
196
                    + ","
197
                    + str(round(float(attributes["Q3"]), 4))
198
                    + ")"
199
                )
200
                self.contents[self.obj + "00000000::" + self.prop] = val
201

202
        elif tag in ["PropertyColor"]:
203
            if self.prop and self.obj and (self.short == 0):
204
                c = int(attributes["value"])
205
                r = float((c >> 24) & 0xFF) / 255.0
206
                g = float((c >> 16) & 0xFF) / 255.0
207
                b = float((c >> 8) & 0xFF) / 255.0
208
                val = str((r, g, b))
209
                self.contents[self.obj + "00000000::" + self.prop] = val
210

211
        elif tag == "Objects":
212
            self.count = attributes["Count"]
213
            self.obj = None
214

215
            # Print all the contents of the document properties
216
            items = self.contents.items()
217
            items = sorted(items)
218
            for key, value in items:
219
                key = self.clean(key)
220
                value = self.clean(value)
221
                print("   " + key + " : " + value)
222
            print("   Objects: (" + self.count + ")")
223
            self.contents = {}
224

225
    def endElement(self, tag):
226

227
        if (tag == "Document") and (self.short != 2):
228
            items = self.contents.items()
229
            items = sorted(items)
230
            for key, value in items:
231
                key = self.clean(key)
232
                if "00000000::" in key:
233
                    key = "   " + key.split("00000000::")[1]
234
                value = self.clean(value)
235
                if value:
236
                    print("        " + key + " : " + value)
237

238
    def clean(self, value):
239

240
        value = value.strip()
241
        return value
242

243

244
if __name__ == "__main__":
245

246
    if len(sys.argv) < 2:
247
        print(__doc__)
248
        sys.exit()
249

250
    if ("-h" in sys.argv[1:]) or ("--help" in sys.argv[1:]):
251
        print(__doc__)
252
        sys.exit()
253

254
    ext = sys.argv[-1].rsplit(".")[-1].lower()
255
    if not ext.startswith("fcstd") and not ext.startswith("fcbak"):
256
        print(__doc__)
257
        sys.exit()
258

259
    if ("-vs" in sys.argv[1:]) or ("--veryshort" in sys.argv[1:]):
260
        short = 2
261
    elif ("-s" in sys.argv[1:]) or ("--short" in sys.argv[1:]):
262
        short = 1
263
    else:
264
        short = 0
265

266
    if ("-g" in sys.argv[1:]) or ("--gui" in sys.argv[1:]):
267
        gui = True
268
    else:
269
        gui = False
270

271
    zfile = zipfile.ZipFile(sys.argv[-1])
272

273
    if not "Document.xml" in zfile.namelist():
274
        sys.exit(1)
275
    doc = zfile.read("Document.xml")
276
    if gui and "GuiDocument.xml" in zfile.namelist():
277
        guidoc = zfile.read("GuiDocument.xml")
278
        guidoc = re.sub(b"<\?xml.*?-->", b" ", guidoc, flags=re.MULTILINE | re.DOTALL)
279
        # a valid xml doc can have only one root element. So we need to insert
280
        # all the contents of the GUiDocument <document> tag into the main one
281
        doc = re.sub(b"<\/Document>", b"", doc, flags=re.MULTILINE | re.DOTALL)
282
        guidoc = re.sub(b"<Document.*?>", b" ", guidoc, flags=re.MULTILINE | re.DOTALL)
283
        doc += guidoc
284
    s = os.path.getsize(sys.argv[-1])
285
    if s < 1024:
286
        s = str(s) + "B"
287
    elif s > 1048576:
288
        s = str(s / 1048576) + "M"
289
    else:
290
        s = str(s / 1024) + "K"
291
    print("Document: " + sys.argv[-1] + " (" + s + ")")
292
    print("   SHA1: " + str(hashlib.sha1(open(sys.argv[-1], "rb").read()).hexdigest()))
293
    xml.sax.parseString(doc, FreeCADFileHandler(zfile, short))
294

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.