diff --git a/src/Mod/Draft/importDXF.py b/src/Mod/Draft/importDXF.py index edde131cdb..3791e910ca 100644 --- a/src/Mod/Draft/importDXF.py +++ b/src/Mod/Draft/importDXF.py @@ -4228,38 +4228,38 @@ class DxfImportReporter: Formats the statistics into a human-readable string for console output. """ if not self.stats: - return "DXF Import: No statistics were returned from the importer.\n" + return "DXF Import: no statistics were returned from the importer.\n" - lines = ["\n--- DXF Import Summary ---"] + lines = ["\n--- DXF import summary ---"] # General info - lines.append(f"DXF Version: {self.stats.get('dxfVersion', 'Unknown')}") - lines.append(f"File Encoding: {self.stats.get('dxfEncoding', 'Unknown')}") + lines.append(f"DXF version: {self.stats.get('dxfVersion', 'Unknown')}") + lines.append(f"File encoding: {self.stats.get('dxfEncoding', 'Unknown')}") # Scaling info file_units = self.stats.get('fileUnits', 'Not specified') source = self.stats.get('scalingSource', '') if source: - lines.append(f"File Units: {file_units} (from {source})") + lines.append(f"File units: {file_units} (from {source})") else: - lines.append(f"File Units: {file_units}") + lines.append(f"File units: {file_units}") manual_scaling = self.stats.get('importSettings', {}).get('Manual scaling factor', '1.0') - lines.append(f"Manual Scaling Factor: {manual_scaling}") + lines.append(f"Manual scaling factor: {manual_scaling}") final_scaling = self.stats.get('finalScalingFactor', 1.0) - lines.append(f"Final Scaling: 1 DXF unit = {final_scaling:.4f} mm") + lines.append(f"Final scaling: 1 DXF unit = {final_scaling:.4f} mm") lines.append("") # Timing lines.append("Performance:") cpp_time = self.stats.get('importTimeSeconds', 0.0) - lines.append(f" - C++ Import Time: {cpp_time:.4f} seconds") - lines.append(f" - Total Import Time: {self.total_time:.4f} seconds") + lines.append(f" - C++ import time: {cpp_time:.4f} seconds") + lines.append(f" - Total import time: {self.total_time:.4f} seconds") lines.append("") # Settings - lines.append("Import Settings:") + lines.append("Import settings:") settings = self.stats.get('importSettings', {}) if settings: for key, value in sorted(settings.items()): @@ -4269,7 +4269,7 @@ class DxfImportReporter: lines.append("") # Counts - lines.append("Entity Counts:") + lines.append("Entity counts:") total_read = 0 entities = self.stats.get('entityCounts', {}) if entities: @@ -4283,15 +4283,31 @@ class DxfImportReporter: lines.append(f"FreeCAD objects created: {self.stats.get('totalEntitiesCreated', 0)}") lines.append("") - lines.append("Unsupported Features:") + lines.append("Import issues and unsupported features:") unsupported = self.stats.get('unsupportedFeatures', {}) if unsupported: - for key, value in sorted(unsupported.items()): - lines.append(f" - {key}: {value} time(s)") - else: - lines.append(" (None)") + for key, occurrences in sorted(unsupported.items()): + count = len(occurrences) + max_details_to_show = 5 - lines.append("--- End of Summary ---\n") + details_list = [] + for i, (line, handle) in enumerate(occurrences): + if i >= max_details_to_show: + break + if handle: + details_list.append(f"line {line} (handle {handle})") + else: + details_list.append(f"line {line} (no handle available)") + + details_str = ", ".join(details_list) + if count > max_details_to_show: + lines.append(f" - {key}: {count} time(s). Examples: {details_str}, ...") + else: + lines.append(f" - {key}: {count} time(s) at {details_str}") + else: + lines.append(" (none)") + + lines.append("--- End of summary ---\n") return "\n".join(lines) def report_to_console(self): diff --git a/src/Mod/Import/App/dxf/ImpExpDxf.cpp b/src/Mod/Import/App/dxf/ImpExpDxf.cpp index 534f2bcf4e..815624f038 100644 --- a/src/Mod/Import/App/dxf/ImpExpDxf.cpp +++ b/src/Mod/Import/App/dxf/ImpExpDxf.cpp @@ -1404,7 +1404,14 @@ Py::Object ImpExpDxfRead::getStatsAsPyObject() // Create a nested dictionary for any unsupported DXF features encountered. Py::Dict unsupportedFeaturesDict; for (const auto& pair : m_stats.unsupportedFeatures) { - unsupportedFeaturesDict.setItem(pair.first.c_str(), Py::Long(pair.second)); + Py::List occurrencesList; + for (const auto& occurrence : pair.second) { + Py::Tuple infoTuple(2); + infoTuple.setItem(0, Py::Long(occurrence.first)); + infoTuple.setItem(1, Py::String(occurrence.second)); + occurrencesList.append(infoTuple); + } + unsupportedFeaturesDict.setItem(pair.first.c_str(), occurrencesList); } statsDict.setItem("unsupportedFeatures", unsupportedFeaturesDict); diff --git a/src/Mod/Import/App/dxf/dxf.cpp b/src/Mod/Import/App/dxf/dxf.cpp index b74284ad1f..41f1f064dc 100644 --- a/src/Mod/Import/App/dxf/dxf.cpp +++ b/src/Mod/Import/App/dxf/dxf.cpp @@ -2342,8 +2342,8 @@ bool CDxfRead::ReadDimension() bool CDxfRead::ReadUnknownEntity() { - UnsupportedFeature("Entity type '%s'", m_record_data); ProcessAllEntityAttributes(); + UnsupportedFeature("Entity type '%s'", m_record_data); return true; } @@ -2403,7 +2403,7 @@ void CDxfRead::UnsupportedFeature(const char* format, args&&... argValuess) { // NOLINTNEXTLINE(runtime/printf) std::string formattedMessage = fmt::sprintf(format, std::forward(argValuess)...); - m_stats.unsupportedFeatures[formattedMessage]++; + m_stats.unsupportedFeatures[formattedMessage].emplace_back(m_line, m_current_entity_handle); } bool CDxfRead::get_next_record() @@ -2798,6 +2798,8 @@ bool CDxfRead::ReadEntity() { InitializeAttributes(); m_entityAttributes.SetDefaults(); + m_current_entity_handle.clear(); + SetupStringAttribute(eHandle, m_current_entity_handle); EntityNormalVector.Set(0, 0, 1); Setup3DVectorAttribute(eExtrusionDirection, EntityNormalVector); SetupStringAttribute(eLinetypeName, m_entityAttributes.m_LineType); diff --git a/src/Mod/Import/App/dxf/dxf.h b/src/Mod/Import/App/dxf/dxf.h index caafca4989..1625822977 100644 --- a/src/Mod/Import/App/dxf/dxf.h +++ b/src/Mod/Import/App/dxf/dxf.h @@ -187,7 +187,7 @@ struct DxfImportStats double finalScalingFactor = 1.0; std::map entityCounts; std::map importSettings; - std::map unsupportedFeatures; + std::map>> unsupportedFeatures; int totalEntitiesCreated = 0; }; @@ -200,6 +200,7 @@ enum eDXFGroupCode_t ePrimaryText = 1, eName = 2, eExtraText = 3, + eHandle = 5, eLinetypeName = 6, eTextStyleName = 7, eLayerName = 8, @@ -462,6 +463,7 @@ private: bool m_not_eof = true; int m_line = 0; bool m_repeat_last_record = false; + std::string m_current_entity_handle; // The scaling from DXF units to millimetres. // This does not include the dxfScaling option