Fix T54477: Broken utf8 strings in old .blend files
authorBastien Montagne <montagne29@wanadoo.fr>
Tue, 3 Apr 2018 13:50:49 +0000 (15:50 +0200)
committerBastien Montagne <montagne29@wanadoo.fr>
Tue, 3 Apr 2018 14:03:11 +0000 (16:03 +0200)
Back in the days (2.4x and before), it was rather easy to get some
invalid utf-8 strings in Blender. This is totally breaking modern code,
so this commit adds a simple 'check & fix strings' operator, available
from the main File menu.

release/scripts/startup/bl_operators/file.py
release/scripts/startup/bl_ui/space_info.py

index 1b51906a0322af95c21ec575bcf9d19b28275feb..4ab8d59f263b0995355a87cddb38402e0a9f2b7e 100644 (file)
@@ -249,7 +249,61 @@ class WM_OT_previews_batch_clear(Operator):
         return {'FINISHED'}
 
 
+class WM_OT_blend_strings_utf8_validate(Operator):
+    """Check and fix all strings in current .blend file to be valid UTF-8 Unicode (needed for some old, 2.4x area files)"""
+    bl_idname = "wm.blend_strings_utf8_validate"
+    bl_label = "Validate .blend strings"
+    bl_options = {'REGISTER'}
+
+    def validate_strings(self, item, done_items):
+        if item is None:
+            return False
+
+        if item in done_items:
+            return False
+        done_items.add(item)
+
+        if getattr(item, 'library', None) is not None:
+            return False  # No point in checking library data, we cannot fix it anyway...
+
+        changed = False
+        for prop in item.bl_rna.properties:
+            if prop.identifier in {'bl_rna', 'rna_type'}:
+                continue  # Or we'd recurse 'till Hell freezes.
+            if prop.is_readonly:
+                continue
+            if prop.type == 'STRING':
+                val_bytes = item.path_resolve(prop.identifier, False).as_bytes()
+                val_utf8 = val_bytes.decode('utf-8', 'replace')
+                val_bytes_valid = val_utf8.encode('utf-8')
+                if val_bytes_valid != val_bytes:
+                    print("found bad utf8 encoded string %r, fixing to %r (%r)..."
+                          "" % (val_bytes, val_bytes_valid, val_utf8))
+                    setattr(item, prop.identifier, val_utf8)
+                    changed = True
+            elif prop.type == 'POINTER':
+                it = getattr(item, prop.identifier)
+                changed |= self.validate_strings(it, done_items)
+            elif prop.type == 'COLLECTION':
+                for it in getattr(item, prop.identifier):
+                    changed |= self.validate_strings(it, done_items)
+        return changed
+
+    def execute(self, context):
+        changed = False
+        done_items = set()
+        for prop in bpy.data.bl_rna.properties:
+            if prop.type == 'COLLECTION':
+                for it in getattr(bpy.data, prop.identifier):
+                    changed |= self.validate_strings(it, done_items)
+        if changed:
+            self.report({'WARNING'},
+                        "Some strings were fixed, don't forget to save the .blend file to keep those changes")
+        return {'FINISHED'}
+
+
 classes = (
     WM_OT_previews_batch_clear,
     WM_OT_previews_batch_generate,
+    WM_OT_blend_strings_utf8_validate,
 )
index a7b518dfd2e3430f0406551eda751e2c299d61e0..180e48af3863ebbd09038176ec49c8770b2ee111 100644 (file)
@@ -154,6 +154,7 @@ class INFO_MT_file(Menu):
         layout.separator()
 
         layout.menu("INFO_MT_file_external_data", icon='EXTERNAL_DATA')
+        layout.operator("wm.blend_strings_utf8_validate", icon='FILE_BLEND')
 
         layout.separator()