37#include <wx/zipstrm.h>
38#include <wx/wfstream.h>
39#include <wx/mstream.h>
40#include <wx/txtstrm.h>
48static const std::vector<wxString> c_deviceAttributesWhitelist = { wxS(
"Value" ),
50 wxS(
"Manufacturer Part" ),
51 wxS(
"Manufacturer" ),
52 wxS(
"BOM_Manufacturer Part" ),
53 wxS(
"BOM_Manufacturer" ),
54 wxS(
"Supplier Part" ),
56 wxS(
"BOM_Supplier Part" ),
57 wxS(
"BOM_Supplier" ),
58 wxS(
"LCSC Part Name" ) };
62static std::string
ToStdString(
const wxString& aStr )
64 return std::string( aStr.ToUTF8() );
68static wxString ToWxString(
const std::string& aStr )
70 return wxString::FromUTF8( aStr.c_str() );
74static nlohmann::json EmptyV3ProjectIndex()
76 nlohmann::json
project = nlohmann::json::object();
77 project[
"schematics"] = nlohmann::json::object();
78 project[
"boards"] = nlohmann::json::object();
79 project[
"pcbs"] = nlohmann::json::object();
80 project[
"symbols"] = nlohmann::json::object();
81 project[
"footprints"] = nlohmann::json::object();
82 project[
"devices"] = nlohmann::json::object();
92 if( row.
type == wxS(
"META" ) )
101 const wxString& aDefault = wxEmptyString )
103 if(
const nlohmann::json* meta = FindMetaRow( aDoc ) )
112 if(
const nlohmann::json* meta = FindMetaRow( aDoc ) )
120 const nlohmann::json& aDefault )
122 if(
const nlohmann::json* meta = FindMetaRow( aDoc ) )
124 auto it = meta->find( aKey );
126 if( it != meta->end() )
138 wxString shortenedName = aProjectName;
139 shortenedName.Replace( wxS(
"ProProject_" ), wxS(
"" ) );
140 shortenedName.Replace( wxS(
"ProDocument_" ), wxS(
"" ) );
141 shortenedName = shortenedName.substr( 0, 10 );
152 wxString key = !libName.empty() ? ( libName +
':' + libReference ) : libReference;
155 libId.
Parse( key,
true );
161std::vector<IMPORT_PROJECT_DESC>
164 std::vector<IMPORT_PROJECT_DESC>
result;
166 std::map<wxString, EASYEDAPRO::PRJ_SCHEMATIC> prjSchematics = aProject.at(
"schematics" );
167 std::map<wxString, EASYEDAPRO::PRJ_BOARD> prjBoards = aProject.at(
"boards" );
169 std::map<wxString, wxString> prjPcbNames;
170 std::map<wxString, nlohmann::json> prjPcbs = aProject.at(
"pcbs" );
172 for(
const auto& [pcbUuid, pcbJsonEntry] : prjPcbs )
174 if( pcbJsonEntry.is_string() )
175 prjPcbNames.emplace( pcbUuid, pcbJsonEntry );
176 else if( pcbJsonEntry.is_object() )
177 prjPcbNames.emplace( pcbUuid, pcbJsonEntry.at(
"title" ) );
180 for(
const auto& [prjName, board] : prjBoards )
184 desc.
PCBId = board.pcb;
187 auto pcbNameIt = prjPcbNames.find( desc.
PCBId );
188 if( pcbNameIt != prjPcbNames.end() )
190 desc.
PCBName = pcbNameIt->second;
193 desc.
PCBName = pcbNameIt->first;
195 prjPcbNames.erase( pcbNameIt );
198 auto schIt = prjSchematics.find( desc.
SchematicId );
199 if( schIt != prjSchematics.end() )
206 prjSchematics.erase( schIt );
209 result.emplace_back( desc );
214 for(
const auto& [pcbId, pcbName] : prjPcbNames )
223 result.emplace_back( desc );
229 for(
const auto& [schId, schData] : prjSchematics )
238 result.emplace_back( desc );
247 const std::set<wxString>& aFileNames )
249 std::shared_ptr<wxZipEntry> entry;
250 wxFFileInputStream in( aZipFileName );
251 wxZipInputStream
zip( in );
253 while( entry.reset(
zip.GetNextEntry() ), entry.get() )
255 wxString
name = entry->GetName();
259 if( aFileNames.find(
name ) != aFileNames.end() )
261 wxMemoryOutputStream memos;
263 wxStreamBuffer* buf = memos.GetOutputStreamBuffer();
265 wxString str = wxString::FromUTF8( (
char*) buf->GetBufferStart(), buf->GetBufferSize() );
267 return nlohmann::json::parse( str );
270 catch( nlohmann::json::exception& e )
274 catch( std::exception& e )
280 return nlohmann::json{};
286 static const std::set<wxString> c_files = { wxS(
"project.json" ), wxS(
"device.json" ),
287 wxS(
"footprint.json" ), wxS(
"symbol.json" ) };
289 nlohmann::json j =
FindJsonFile( aZipFileName, c_files );
294 THROW_IO_ERRORF(
_(
"'%s' does not appear to be a valid EasyEDA (JLCEDA) Pro project or library file. "
295 "Cannot find project.json or device.json." ), aZipFileName );
300 const wxString& aFileName,
301 std::function<
bool(
const wxString&,
const wxString&, wxInputStream& )> aCallback )
303 std::shared_ptr<wxZipEntry> entry;
304 wxFFileInputStream in( aFileName );
305 wxZipInputStream
zip( in );
310 while( entry.reset(
zip.GetNextEntry() ), entry.get() )
312 wxString
name = entry->GetName();
313 wxString baseName =
name.AfterLast(
'\\' ).AfterLast(
'/' ).BeforeFirst(
'.' );
317 if( aCallback(
name, baseName,
zip ) )
320 catch( nlohmann::json::exception& e )
324 catch( std::exception& e )
334 wxTextInputStream txt( aInput, wxS(
" " ), wxConvUTF8 );
338 std::vector<nlohmann::json> lines;
339 while( aInput.CanRead() )
343 wxString line = txt.ReadLine();
345 if( !line.IsEmpty() )
347 nlohmann::json js = nlohmann::json::parse( line );
348 lines.emplace_back( js );
352 lines.emplace_back( nlohmann::json() );
355 catch( nlohmann::json::exception& e )
357 wxLogTrace(
traceEasyEdaIo, wxT(
"Cannot parse JSON line %d in '%s': %s" ),
358 currentLine, aSource, e.what() );
368std::vector<std::vector<nlohmann::json>>
371 wxTextInputStream txt( aInput, wxS(
" " ), wxConvUTF8 );
375 std::vector<std::vector<nlohmann::json>> lineBlocks;
376 lineBlocks.emplace_back();
378 while( aInput.CanRead() )
382 wxString line = txt.ReadLine();
384 if( !line.IsEmpty() )
386 nlohmann::json js = nlohmann::json::parse( line );
387 lineBlocks.back().emplace_back( js );
391 lineBlocks.emplace_back();
394 catch( nlohmann::json::exception& e )
396 wxLogTrace(
traceEasyEdaIo, wxT(
"Cannot parse JSON line %d in '%s': %s" ),
397 currentLine, aSource, e.what() );
407std::map<wxString, wxString>
410 std::map<wxString, wxString> stringMap;
412 for(
auto& [key, value] : aInput )
414 if( value.is_string() )
415 stringMap[key] = value.get<wxString>();
416 else if( value.is_number() )
417 stringMap[key] = wxString::FromCDouble( value.get<
double>() );
426 nlohmann::json
project = EmptyV3ProjectIndex();
436 std::map<wxString, std::vector<SHEET_INFO>> sheetsBySch;
439 for(
const auto& [uuid, pageDoc] : aParser.
GetRawDocs( wxS(
"SCH_PAGE" ) ) )
441 wxString schematic = MetaGetString( pageDoc,
"schematic" );
443 if( schematic.empty() )
444 schematic =
V3GetString( pageDoc.head,
"schematic" );
446 if( schematic.empty() )
451 info.name = MetaGetString( pageDoc,
"title", uuid );
452 info.zIndex = MetaGetInt( pageDoc,
"zIndex", 0 );
453 info.order = pageOrder++;
455 sheetsBySch[schematic].push_back( std::move(
info ) );
458 std::map<wxString, wxString> schematicsByBoard;
460 for(
const auto& [uuid, schDoc] : aParser.
GetRawDocs( wxS(
"SCH" ) ) )
462 nlohmann::json sch = nlohmann::json::object();
463 sch[
"name"] =
ToStdString( MetaGetString( schDoc,
"title", uuid ) );
464 sch[
"sheets"] = nlohmann::json::array();
466 auto pagesIt = sheetsBySch.find( uuid );
468 if( pagesIt != sheetsBySch.end() )
470 auto& pages = pagesIt->second;
472 std::sort( pages.begin(), pages.end(),
473 [](
const SHEET_INFO& aLeft,
const SHEET_INFO& aRight )
475 if( aLeft.zIndex != aRight.zIndex )
476 return aLeft.zIndex < aRight.zIndex;
478 return aLeft.order < aRight.order;
483 for(
const SHEET_INFO& page : pages )
485 sch[
"sheets"].push_back( nlohmann::json::object( { {
"id", sheetId++ },
493 wxString board = MetaGetString( schDoc,
"board" );
496 schematicsByBoard[board] = uuid;
499 std::map<wxString, wxString> boardTitles;
501 for(
const auto& [uuid, boardDoc] : aParser.
GetRawDocs( wxS(
"BOARD" ) ) )
502 boardTitles[uuid] = MetaGetString( boardDoc,
"title", uuid );
504 std::map<wxString, wxString> pcbsByBoard;
506 for(
const auto& [uuid, pcbDoc] : aParser.
GetRawDocs( wxS(
"PCB" ) ) )
508 nlohmann::json pcb = nlohmann::json::object();
509 pcb[
"title"] =
ToStdString( MetaGetString( pcbDoc,
"title", uuid ) );
513 wxString board = MetaGetString( pcbDoc,
"board" );
516 pcbsByBoard[board] = uuid;
519 std::set<wxString> allBoardRefs;
521 for(
const auto& [boardRef, schUuid] : schematicsByBoard )
522 allBoardRefs.insert( boardRef );
524 for(
const auto& [boardRef, pcbUuid] : pcbsByBoard )
525 allBoardRefs.insert( boardRef );
527 for(
const wxString& boardRef : allBoardRefs )
529 wxString boardName = boardRef;
531 if(
auto it = boardTitles.find( boardRef ); it != boardTitles.end() )
532 boardName = it->second;
534 if( boardName.empty() )
535 boardName = boardRef;
537 nlohmann::json board = nlohmann::json::object();
538 auto schIt = schematicsByBoard.find( boardRef );
539 auto pcbIt = pcbsByBoard.find( boardRef );
541 board[
"schematic"] = schIt != schematicsByBoard.end() ?
ToStdString( schIt->second ) :
"";
542 board[
"pcb"] = pcbIt != pcbsByBoard.end() ?
ToStdString( pcbIt->second ) :
"";
548 &&
project[
"schematics"].is_object() &&
project[
"schematics"].size() == 1 )
550 auto pcbIt =
project[
"pcbs"].begin();
551 auto schIt =
project[
"schematics"].begin();
553 wxString pcbId = wxString::FromUTF8( pcbIt.key() );
554 wxString schId = wxString::FromUTF8( schIt.key() );
555 wxString boardName = wxString::FromUTF8( pcbIt.value().value(
"title", pcbIt.key() ) );
557 if( boardName.empty() )
560 nlohmann::json board = nlohmann::json::object();
567 if( !aIncludeLibraryMetadata )
570 for(
const auto& [uuid, symDoc] : aParser.
GetRawDocs( wxS(
"SYMBOL" ) ) )
572 nlohmann::json sym = nlohmann::json::object();
573 sym[
"source"] =
ToStdString( MetaGetString( symDoc,
"source" ) );
574 sym[
"description"] =
ToStdString( MetaGetString( symDoc,
"description" ) );
575 sym[
"title"] =
ToStdString( MetaGetString( symDoc,
"title", uuid ) );
576 sym[
"display_title"] = sym[
"title"];
577 sym[
"version"] =
"3";
579 sym[
"tags"] = MetaGetValue( symDoc,
"tags", nlohmann::json::object() );
584 for(
const auto& [uuid, fpDoc] : aParser.
GetRawDocs( wxS(
"FOOTPRINT" ) ) )
586 nlohmann::json fp = nlohmann::json::object();
587 fp[
"source"] =
ToStdString( MetaGetString( fpDoc,
"source" ) );
588 fp[
"description"] =
ToStdString( MetaGetString( fpDoc,
"description" ) );
589 fp[
"title"] =
ToStdString( MetaGetString( fpDoc,
"title", uuid ) );
590 fp[
"display_title"] = fp[
"title"];
593 fp[
"tags"] = MetaGetValue( fpDoc,
"tags", nlohmann::json::object() );
598 for(
const auto& [uuid, deviceDoc] : aParser.
GetRawDocs( wxS(
"DEVICE" ) ) )
600 nlohmann::json dev = nlohmann::json::object();
601 dev[
"source"] =
ToStdString( MetaGetString( deviceDoc,
"source" ) );
602 dev[
"description"] =
ToStdString( MetaGetString( deviceDoc,
"description" ) );
603 dev[
"title"] =
ToStdString( MetaGetString( deviceDoc,
"title", uuid ) );
604 dev[
"version"] =
"3";
605 dev[
"tags"] = MetaGetValue( deviceDoc,
"tags", nlohmann::json::object() );
606 dev[
"attributes"] = MetaGetValue( deviceDoc,
"attributes", nlohmann::json::object() );
617 std::map<wxString, BLOB> blobs;
619 for(
const auto& [blobDocUuid, rawDoc] : aParser.
GetRawDocs( wxS(
"BLOB" ) ) )
621 for(
const V3_ROW& row : rawDoc.rows )
623 if( row.
type != wxS(
"BLOB" ) )
633 catch( nlohmann::json::exception& e )
635 wxLogTrace(
traceEasyEdaIo, wxT(
"EasyEDA Pro v3 blob in '%s' was skipped due to parse error: %s" ),
636 blobDocUuid, e.what() );
661 if( !aTags.is_object() )
666 auto appendTagName = [&](
const char* aKey )
668 if( !aTags.contains( aKey ) || !aTags.at( aKey ).is_object() )
676 if( !keywords.empty() )
677 keywords += wxS(
" " );
682 appendTagName(
"parent_tag" );
683 appendTagName(
"child_tag" );
690 const std::map<wxString, wxString>& aDeviceAttributes )
692 wxString inputText = aInput;
693 wxString resolvedText;
694 int variableCount = 0;
699 if( !inputText.StartsWith( wxS(
"={" ) ) )
702 resolvedText.Clear();
705 for(
size_t i = 1; i < inputText.size(); )
707 wxUniChar c = inputText[i++];
712 bool endFound =
false;
714 while( i < inputText.size() )
731 get_def( aDeviceAttributes, varName, wxString::Format( wxS(
"{%s!}" ), varName ) );
733 resolvedText << varValue;
742 inputText = resolvedText;
743 }
while( variableCount > 0 );
752 aText.Replace( wxS(
"\u2103" ), wxS(
"\u00B0C" ),
true );
758 const wxString& aFallback )
760 wxString uniqueBase = aName.empty() ? aFallback : aName;
761 wxString uniqueName = uniqueBase;
764 while( aUsedNames.contains( uniqueName ) )
765 uniqueName = uniqueBase + wxString::Format( wxS(
"_%d" ), suffix++ );
767 aUsedNames.insert( uniqueName );
773 const std::map<wxString, PRJ_DEVICE>& aDevices,
774 std::set<wxString>* aUsedNames )
776 std::map<wxString, wxString> deviceLibNames;
777 std::set<wxString> localUsedNames;
778 std::set<wxString>& usedLibNames = aUsedNames ? *aUsedNames : localUsedNames;
779 nlohmann::json deviceLibNamesJson = nlohmann::json::object();
781 for(
const auto& [devUuid, device] : aDevices )
783 wxString libNameForDevice =
MakeUniqueLibName( usedLibNames, device.title, devUuid );
784 deviceLibNames[devUuid] = libNameForDevice;
785 deviceLibNamesJson[std::string( devUuid.ToUTF8() )] = std::string( libNameForDevice.ToUTF8() );
788 aProject[
"device_lib_names"] = std::move( deviceLibNamesJson );
789 return deviceLibNames;
795 if( aDeviceUuid.empty() || !aProject.contains(
"device_lib_names" )
796 || !aProject.at(
"device_lib_names" ).is_object() )
801 std::string deviceIdUtf8 = std::string( aDeviceUuid.ToUTF8() );
802 const auto& names = aProject.at(
"device_lib_names" );
804 if( names.contains( deviceIdUtf8 ) && names.at( deviceIdUtf8 ).is_string() )
805 return names.at( deviceIdUtf8 ).get<wxString>();
812 const wxString& aDeviceUuid )
816 if( aDeviceUuid.empty() || !aProject.contains(
"devices" ) || !aProject.at(
"devices" ).is_object() )
819 std::string deviceIdUtf8 = std::string( aDeviceUuid.ToUTF8() );
821 if( !aProject.at(
"devices" ).contains( deviceIdUtf8 ) )
824 const nlohmann::json& dev = aProject.at(
"devices" ).at( deviceIdUtf8 );
829 if( dev.contains(
"attributes" ) && dev.at(
"attributes" ).is_object() )
831 for(
const auto& [key, value] : dev.at(
"attributes" ).items() )
841 wxString valueText =
get_def( aDeviceAttributes, wxS(
"Value" ), wxEmptyString );
843 if( valueText.empty() )
844 valueText =
get_def( aDeviceAttributes, wxS(
"Name" ), wxEmptyString );
851 const std::map<wxString, wxString>& aDeviceAttributes,
bool aIncludeValue,
852 const std::function<
void(
const wxString& aKey,
const wxString& aValue )>& aCallback )
854 for(
const wxString& attrKey : c_deviceAttributesWhitelist )
856 if( !aIncludeValue && attrKey == wxS(
"Value" ) )
859 auto valOpt =
get_opt( aDeviceAttributes, attrKey );
861 if( !valOpt || valOpt->empty() )
870template <
typename Map>
873 wxString uniqueBase = aName.empty() ? aFallback : aName;
874 wxString uniqueName = uniqueBase;
877 while( aItems.contains( uniqueName ) )
878 uniqueName = uniqueBase + wxString::Format( wxS(
"_%d" ), suffix++ );
886 std::map<wxString, V3_SYMBOL_LIB_ITEM> items;
889 const bool hasDevices =
index.is_object() &&
index.contains(
"devices" ) &&
index.at(
"devices" ).is_object()
890 && !
index.at(
"devices" ).empty();
896 for(
const auto& [devUuidKey, deviceJson] :
index.at(
"devices" ).items() )
898 if( !deviceJson.is_object() )
902 auto symbolIt = device.
attributes.find( wxS(
"Symbol" ) );
904 if( symbolIt == device.
attributes.end() || symbolIt->second.empty() )
907 if( !aParser.
FindRawDoc( wxS(
"SYMBOL" ), symbolIt->second ) )
910 wxString deviceUuid =
V3GetString( deviceJson,
"uuid", ToWxString( devUuidKey ) );
914 item.
device = std::move( device );
920 catch( nlohmann::json::exception& e )
922 wxLogTrace(
traceEasyEdaIo, wxT(
"Failed to parse EasyEDA Pro v3 device metadata: %s" ), e.what() );
933 items.emplace(
name, std::move( item ) );
941 const wxString& aDocType )
943 std::map<wxString, wxString> titlesByUuid;
946 if(
index.is_object() &&
index.contains( aIndexKey ) &&
index.at( aIndexKey ).is_object() )
948 for(
const auto& [uuidKey, metadata] :
index.at( aIndexKey ).items() )
950 wxString uuid =
V3GetString( metadata,
"uuid", ToWxString( uuidKey ) );
955 std::map<wxString, wxString> nameToUuid;
957 for(
const auto& [uuid, rawDoc] : aParser.
GetRawDocs( aDocType ) )
961 wxString
name = uuid;
963 if(
auto it = titlesByUuid.find( uuid ); it != titlesByUuid.end() )
Parses EasyEDA Pro v3 .epro2 archives.
const V3_DOC_RAW * FindRawDoc(const wxString &aDocType, const wxString &aUuid) const
const std::map< wxString, V3_DOC_RAW > & GetRawDocs(const wxString &aDocType) const
const nlohmann::json & GetLibraryIndex() const
A logical library item identifier and consists of various portions much like a URI.
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
static UTF8 FixIllegalChars(const UTF8 &aLibItemName, bool aLib)
Replace illegal LIB_ID item name characters with underscores '_'.
static bool empty(const wxTextEntryBase *aCtrl)
static wxString MakeUniqueV3LibraryName(const Map &aItems, const wxString &aName, const wxString &aFallback)
static std::string ToStdString(const wxString &aStr)
const wxChar *const traceEasyEdaIo
#define THROW_IO_ERRORF(msg,...)
wxString get_def(const std::map< wxString, wxString > &aMap, const char *aKey, const char *aDefval="")
std::optional< V > get_opt(const std::map< wxString, V > &aMap, const wxString &aKey)
nlohmann::json BuildV3ProjectIndexFromRawDocs(const V3_DOC_PARSER &aParser, bool aIncludeLibraryMetadata=true)
Build a minimal legacy-style project index from parsed v3 raw documents.
LIB_ID ToKiCadLibID(const wxString &aLibName, const wxString &aLibReference)
wxString MakeUniqueLibName(std::set< wxString > &aUsedNames, const wxString &aName, const wxString &aFallback)
Allocate a unique library item name, recording it in aUsedNames.
wxString KeywordsFromV3Tags(const nlohmann::json &aTags)
Build KiCad keywords from a v3 tags object (parent_tag / child_tag name fields).
wxString GetV3LibraryItemTitle(const nlohmann::json &aMetadata, const wxString &aUuid)
void ForEachImportedDeviceField(const std::map< wxString, wxString > &aDeviceAttributes, bool aIncludeValue, const std::function< void(const wxString &aKey, const wxString &aValue)> &aCallback)
Invoke aCallback for each non-empty whitelisted Device field (resolved + normalized).
wxString ResolveDeviceFieldVariables(const wxString &aInput, const std::map< wxString, wxString > &aDeviceAttributes)
Resolve EasyEDA ={Var} / ={A}text{B} field expressions against device attributes.
void IterateZipFiles(const wxString &aFileName, std::function< bool(const wxString &, const wxString &, wxInputStream &)> aCallback)
std::vector< nlohmann::json > ParseJsonLines(wxInputStream &aInput, const wxString &aSource)
wxString NormalizeEasyEDAText(wxString aText)
Replace EasyEDA temperature glyph (℃) with °C.
V3_DEVICE_DATA GetV3DeviceData(const nlohmann::json &aProject, const wxString &aDeviceUuid)
std::vector< std::vector< nlohmann::json > > ParseJsonLinesWithSeparation(wxInputStream &aInput, const wxString &aSource)
Multiple document types (e.g.
wxString LookupV3DeviceLibName(const nlohmann::json &aProject, const wxString &aDeviceUuid)
nlohmann::json FindJsonFile(const wxString &aZipFileName, const std::set< wxString > &aFileNames)
std::vector< IMPORT_PROJECT_DESC > ProjectToSelectorDialog(const nlohmann::json &aProject, bool aPcbOnly=false, bool aSchOnly=false)
nlohmann::json ReadProjectOrDeviceFile(const wxString &aZipFileName)
wxString ShortenLibName(wxString aProjectName)
std::map< wxString, wxString > AnyMapToStringMap(const std::map< wxString, nlohmann::json > &aInput)
wxString V3JsonToString(const nlohmann::json &aValue, const wxString &aDefault)
wxString ResolveV3DeviceValueText(const std::map< wxString, wxString > &aDeviceAttributes)
Preferred Value text: Value attribute, else Name, with variables resolved.
int V3GetInt(const nlohmann::json &aObj, const char *aKey, int aDefault)
std::map< wxString, V3_SYMBOL_LIB_ITEM > BuildV3SymbolLibraryMap(const V3_DOC_PARSER &aParser)
std::map< wxString, wxString > BuildV3LibraryItemMap(const V3_DOC_PARSER &aParser, const char *aIndexKey, const wxString &aDocType)
std::map< wxString, BLOB > BuildV3BlobMap(const V3_DOC_PARSER &aParser)
wxString V3GetString(const nlohmann::json &aObj, const char *aKey, const wxString &aDefault)
std::map< wxString, wxString > BuildV3DeviceLibNames(nlohmann::json &aProject, const std::map< wxString, PRJ_DEVICE > &aDevices, std::set< wxString > *aUsedNames=nullptr)
Stable project-lib item names keyed by Device UUID (one entry per Device).
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
std::map< wxString, wxString > attributes
std::map< wxString, wxString > attributes
Raw parsed document from an EasyEDA Pro v3 .epru stream.
std::vector< V3_ROW > rows
One parsed row from an EasyEDA Pro v3 .epru document stream.
Build the schematic-library name map for a v3 .elibz2.
Describes how non-KiCad boards and schematics should be imported as KiCad projects.
wxString result
Test unit parsing edge cases and error handling.
wxLogTrace helper definitions.