KiCad PCB EDA Suite
Loading...
Searching...
No Matches
legacy_format.h
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#pragma once
21
22#include <algorithm>
23#include <optional>
24#include <string>
25#include <vector>
26
27#include <wx/base64.h>
28#include <wx/stream.h>
29
30#include <base_units.h>
31#include <common.h>
32#include <embedded_files.h>
34#include <ki_exception.h>
35#include <kiid.h>
36#include <lib_id.h>
37#include <mmh3_hash.h>
38#include <page_info.h>
39#include <richio.h>
40#include <string_utils.h>
41#include <stroke_params.h>
42#include <title_block.h>
43
45{
46
47// Serialization grammar from 9.0.0 ccafeabf1503 and 10.0.7 93a8a4827b0b.
48// Only primitive quoting and number conversion are shared with the current implementation.
49
50inline constexpr int OMIT_HIDE = 1 << 6;
51
52
53inline bool IsDefaultTextV10( const EDA_TEXT& aText )
54{
55 return !aText.IsMirrored() && aText.GetHorizJustify() == GR_TEXT_H_ALIGN_CENTER
56 && aText.GetVertJustify() == GR_TEXT_V_ALIGN_CENTER && aText.GetAutoThickness() && !aText.IsItalic()
57 && !aText.IsBold() && !aText.IsMultilineAllowed() && aText.GetFontName().IsEmpty();
58}
59
60inline void FormatBool( OUTPUTFORMATTER* aOut, const wxString& aKey, bool aValue )
61{
62 aOut->Print( "(%ls %s)", aKey.wc_str(), aValue ? "yes" : "no" );
63}
64
65
66inline void FormatOptBool( OUTPUTFORMATTER* aOut, const wxString& aKey, std::optional<bool> aValue )
67{
68 if( aValue.has_value() )
69 FormatBool( aOut, aKey, *aValue );
70 else
71 aOut->Print( "(%ls none)", aKey.wc_str() );
72}
73
74
75inline void FormatUuid( OUTPUTFORMATTER* aOut, const KIID& aUuid )
76{
77 aOut->Print( "(uuid %s)", aOut->Quotew( aUuid.AsString() ).c_str() );
78}
79
80
81inline UTF8 FormatLibId( const LIB_ID& aId )
82{
84
85 if( !aId.GetLibNickname().empty() )
86 {
87 result += aId.GetLibNickname();
88 result += ':';
89 }
90
91 result += aId.GetLibItemName();
92 return result;
93}
94
95
96inline void FormatStreamData( OUTPUTFORMATTER& aOut, const wxStreamBuffer& aStream )
97{
98 aOut.Print( "(data" );
99 const wxString encoded = wxBase64Encode( aStream.GetBufferStart(), aStream.GetBufferSize() );
100
101 for( size_t first = 0; first < encoded.Length(); first += 76 )
102 aOut.Print( "\n\"%s\"", TO_UTF8( encoded( first, 76 ) ) );
103
104 aOut.Print( ")" );
105}
106
107
108inline const char* LineStyleToken( LINE_STYLE aStyle )
109{
110 switch( aStyle )
111 {
112 case LINE_STYLE::DASH: return "dash";
113 case LINE_STYLE::DOT: return "dot";
114 case LINE_STYLE::DASHDOT: return "dash_dot";
115 case LINE_STYLE::DASHDOTDOT: return "dash_dot_dot";
116 case LINE_STYLE::SOLID: return "solid";
117 case LINE_STYLE::DEFAULT: return "default";
118 }
119
120 THROW_IO_ERROR( wxT( "Unreviewed line style in a frozen writer." ) );
121}
122
123
124// Stroke and title-block grammar is identical in both pinned releases.
125inline void FormatStroke( OUTPUTFORMATTER* aOut, const STROKE_PARAMS& aStroke, const EDA_IU_SCALE& aScale )
126{
127 const auto color = aStroke.GetColor();
128
129 if( color == KIGFX::COLOR4D::UNSPECIFIED )
130 {
131 aOut->Print( "(stroke (width %s) (type %s))",
132 EDA_UNIT_UTILS::FormatInternalUnits( aScale, aStroke.GetWidth() ).c_str(),
133 LineStyleToken( aStroke.GetLineStyle() ) );
134 }
135 else
136 {
137 aOut->Print( "(stroke (width %s) (type %s) (color %d %d %d %s))",
138 EDA_UNIT_UTILS::FormatInternalUnits( aScale, aStroke.GetWidth() ).c_str(),
139 LineStyleToken( aStroke.GetLineStyle() ), KiROUND( color.r * 255.0 ), KiROUND( color.g * 255.0 ),
140 KiROUND( color.b * 255.0 ), FormatDouble2Str( color.a ).c_str() );
141 }
142}
143
144
145inline const char* PageTypeToken( PAGE_SIZE_TYPE aType )
146{
147 switch( aType )
148 {
149 case PAGE_SIZE_TYPE::A5: return "A5";
150 case PAGE_SIZE_TYPE::A4: return "A4";
151 case PAGE_SIZE_TYPE::A3: return "A3";
152 case PAGE_SIZE_TYPE::A2: return "A2";
153 case PAGE_SIZE_TYPE::A1: return "A1";
154 case PAGE_SIZE_TYPE::A0: return "A0";
155 case PAGE_SIZE_TYPE::A: return "A";
156 case PAGE_SIZE_TYPE::B: return "B";
157 case PAGE_SIZE_TYPE::C: return "C";
158 case PAGE_SIZE_TYPE::D: return "D";
159 case PAGE_SIZE_TYPE::E: return "E";
160 case PAGE_SIZE_TYPE::GERBER: return "GERBER";
161 case PAGE_SIZE_TYPE::USLetter: return "USLetter";
162 case PAGE_SIZE_TYPE::USLegal: return "USLegal";
163 case PAGE_SIZE_TYPE::USLedger: return "USLedger";
164 case PAGE_SIZE_TYPE::User: return "User";
165 }
166
167 THROW_IO_ERROR( wxT( "Unreviewed paper type in a frozen writer." ) );
168}
169
170
171inline void FormatPageV9( OUTPUTFORMATTER* aOut, const PAGE_INFO& aPage )
172{
173 aOut->Print( "(paper %s", aOut->Quotew( PageTypeToken( aPage.GetType() ) ).c_str() );
174
175 if( aPage.GetType() == PAGE_SIZE_TYPE::User )
176 aOut->Print( " %g %g", aPage.GetWidthMils() * 25.4 / 1000.0, aPage.GetHeightMils() * 25.4 / 1000.0 );
177
178 if( aPage.GetType() != PAGE_SIZE_TYPE::User && aPage.IsPortrait() )
179 aOut->Print( " portrait" );
180
181 aOut->Print( ")" );
182}
183
184
185inline void FormatPageV10( OUTPUTFORMATTER* aOut, const PAGE_INFO& aPage )
186{
187 aOut->Print( "(paper %s", aOut->Quotew( PageTypeToken( aPage.GetType() ) ).c_str() );
188
189 if( aPage.GetType() == PAGE_SIZE_TYPE::User )
190 {
191 aOut->Print( " %s %s", FormatDouble2Str( aPage.GetWidthMils() * 25.4 / 1000.0 ).c_str(),
192 FormatDouble2Str( aPage.GetHeightMils() * 25.4 / 1000.0 ).c_str() );
193 }
194
195 if( aPage.GetType() != PAGE_SIZE_TYPE::User && aPage.IsPortrait() )
196 aOut->Print( " portrait" );
197
198 aOut->Print( ")" );
199}
200
201
202inline void FormatTitle( OUTPUTFORMATTER* aOut, const TITLE_BLOCK& aTitle )
203{
204 bool empty = aTitle.GetTitle().IsEmpty() && aTitle.GetDate().IsEmpty() && aTitle.GetRevision().IsEmpty()
205 && aTitle.GetCompany().IsEmpty();
206
207 for( int ii = 0; ii < 9; ++ii )
208 empty = empty && aTitle.GetComment( ii ).IsEmpty();
209
210 if( empty )
211 return;
212
213 aOut->Print( "(title_block" );
214
215 if( !aTitle.GetTitle().IsEmpty() )
216 aOut->Print( "(title %s)", aOut->Quotew( aTitle.GetTitle() ).c_str() );
217
218 if( !aTitle.GetDate().IsEmpty() )
219 aOut->Print( "(date %s)", aOut->Quotew( aTitle.GetDate() ).c_str() );
220
221 if( !aTitle.GetRevision().IsEmpty() )
222 aOut->Print( "(rev %s)", aOut->Quotew( aTitle.GetRevision() ).c_str() );
223
224 if( !aTitle.GetCompany().IsEmpty() )
225 aOut->Print( "(company %s)", aOut->Quotew( aTitle.GetCompany() ).c_str() );
226
227 for( int ii = 0; ii < 9; ++ii )
228 {
229 if( !aTitle.GetComment( ii ).IsEmpty() )
230 aOut->Print( "(comment %d %s)", ii + 1, aOut->Quotew( aTitle.GetComment( ii ) ).c_str() );
231 }
232
233 aOut->Print( ")" );
234}
235
236
237inline void FormatText( OUTPUTFORMATTER* aOut, const EDA_TEXT& aText, const EDA_IU_SCALE& aScale, int aControlBits,
238 bool aV9 )
239{
240 aOut->Print( "(effects(font" );
241
242 if( aText.GetFont() && !aText.GetFont()->GetName().IsEmpty() )
243 aOut->Print( "(face %s)", aOut->Quotew( aText.GetFont()->GetName() ).c_str() );
244
245 aOut->Print( "(size %s %s)", EDA_UNIT_UTILS::FormatInternalUnits( aScale, aText.GetTextHeight() ).c_str(),
246 EDA_UNIT_UTILS::FormatInternalUnits( aScale, aText.GetTextWidth() ).c_str() );
247
248 if( aText.GetLineSpacing() != 1.0 )
249 aOut->Print( "(line_spacing %s)", FormatDouble2Str( aText.GetLineSpacing() ).c_str() );
250
251 const int thickness = GetLegacyTextThickness( aText );
252
253 if( aV9 ? aText.GetTextThickness() != 0 : !aText.GetAutoThickness() )
254 aOut->Print( "(thickness %s)", EDA_UNIT_UTILS::FormatInternalUnits( aScale, thickness ).c_str() );
255
256 if( aText.IsBold() )
257 FormatBool( aOut, "bold", true );
258
259 if( aText.IsItalic() )
260 FormatBool( aOut, "italic", true );
261
262 if( !( aControlBits & ( 1 << 11 ) ) && aText.GetTextColor() != KIGFX::COLOR4D::UNSPECIFIED )
263 {
264 const auto color = aText.GetTextColor();
265 aOut->Print( "(color %d %d %d %s)", KiROUND( color.r * 255.0 ), KiROUND( color.g * 255.0 ),
266 KiROUND( color.b * 255.0 ), FormatDouble2Str( color.a ).c_str() );
267 }
268
269 aOut->Print( ")" );
270
271 if( aText.IsMirrored() || aText.GetHorizJustify() != GR_TEXT_H_ALIGN_CENTER
273 {
274 aOut->Print( "(justify" );
275
277 aOut->Print( aText.GetHorizJustify() == GR_TEXT_H_ALIGN_LEFT ? " left" : " right" );
278
280 aOut->Print( aText.GetVertJustify() == GR_TEXT_V_ALIGN_TOP ? " top" : " bottom" );
281
282 if( aText.IsMirrored() )
283 aOut->Print( " mirror" );
284
285 aOut->Print( ")" );
286 }
287
288 if( aV9 && !( aControlBits & OMIT_HIDE ) && !aText.IsVisible() )
289 FormatBool( aOut, "hide", true );
290
291 if( !( aControlBits & ( 1 << 12 ) ) && aText.HasHyperlink() )
292 aOut->Print( "(href %s)", aOut->Quotew( aText.GetHyperlink() ).c_str() );
293
294 aOut->Print( ")" );
295}
296
297
298inline void FormatTextV9( OUTPUTFORMATTER* aOut, const EDA_TEXT& aText, const EDA_IU_SCALE& aScale, int aControlBits )
299{
300 FormatText( aOut, aText, aScale, aControlBits, true );
301}
302
303
304inline void FormatTextV10( OUTPUTFORMATTER* aOut, const EDA_TEXT& aText, const EDA_IU_SCALE& aScale, int aControlBits )
305{
306 FormatText( aOut, aText, aScale, aControlBits, false );
307}
308
309
310inline std::string EmbeddedFileChecksum( const EMBEDDED_FILES::EMBEDDED_FILE& aFile )
311{
312 if( aFile.compressedEncodedData.empty() )
313 return aFile.data_hash;
314
315 const std::vector<char>* payload = &aFile.decompressedData;
317
318 if( payload->empty() )
319 {
320 // Decode a scratch copy so zero-byte and encoded-only attachments leave the source intact.
321 decoded = aFile;
322
324 THROW_IO_ERROR( wxT( "Cannot decode embedded file for an older KiCad version: " ) + aFile.name );
325
326 payload = &decoded.decompressedData;
327 }
328
329 // The target readers use the checksum from before the MMH3 tail-byte correction.
331 hash.addDataV1( reinterpret_cast<const uint8_t*>( payload->data() ), payload->size() );
332 return hash.digest().ToString();
333}
334
335
336inline void FormatEmbeddedFiles( OUTPUTFORMATTER& aOut, const EMBEDDED_FILES& aFiles, bool aWriteData, bool aV9 )
337{
338 aOut.Print( "(embedded_files " );
339
340 for( const auto& [name, entry] : aFiles.EmbeddedFileMap() )
341 {
342 const auto& file = *entry;
343
344 if( !aV9 && file.compressedEncodedData.empty() )
345 continue;
346
347 // The 9.0 reader requires bar delimiters and cannot read the old writer's empty data node.
348 if( aV9 && aWriteData && file.compressedEncodedData.empty() )
349 THROW_IO_ERROR( wxT( "Empty embedded file data cannot be written for KiCad 9." ) );
350
351 const char* type = nullptr;
352
353 switch( file.type )
354 {
355 case EMBEDDED_FILES::EMBEDDED_FILE::FILE_TYPE::DATASHEET: type = "datasheet"; break;
356 case EMBEDDED_FILES::EMBEDDED_FILE::FILE_TYPE::FONT: type = "font"; break;
357 case EMBEDDED_FILES::EMBEDDED_FILE::FILE_TYPE::MODEL: type = "model"; break;
358 case EMBEDDED_FILES::EMBEDDED_FILE::FILE_TYPE::WORKSHEET: type = "worksheet"; break;
359 case EMBEDDED_FILES::EMBEDDED_FILE::FILE_TYPE::OTHER: type = "other"; break;
360 default: THROW_IO_ERROR( wxT( "Unreviewed embedded file type in a frozen writer." ) );
361 }
362
363 const std::string checksum = EmbeddedFileChecksum( file );
364 aOut.Print( "(file (name %s)(type %s)", aOut.Quotew( file.name ).c_str(), type );
365
366 if( aWriteData )
367 {
368 aOut.Print( "(data" );
369
370 for( size_t first = 0; first < file.compressedEncodedData.length(); first += 76 )
371 {
372 const size_t remaining = file.compressedEncodedData.length() - first;
373 const int length = static_cast<int>( std::min<size_t>( remaining, 76 ) );
374 aOut.Print( "\n%1s%.*s%s\n", first ? "" : "|", length, file.compressedEncodedData.data() + first,
375 remaining == size_t( length ) ? "|" : "" );
376 }
377
378 aOut.Print( ")" );
379 }
380
381 aOut.Print( "(checksum %s))", aOut.Quotew( checksum ).c_str() );
382 }
383
384 aOut.Print( ")" );
385}
386
387
388inline void FormatEmbeddedFilesV9( OUTPUTFORMATTER& aOut, const EMBEDDED_FILES& aFiles, bool aWriteData )
389{
390 FormatEmbeddedFiles( aOut, aFiles, aWriteData, true );
391}
392
393
394inline void FormatEmbeddedFilesV10( OUTPUTFORMATTER& aOut, const EMBEDDED_FILES& aFiles, bool aWriteData )
395{
396 FormatEmbeddedFiles( aOut, aFiles, aWriteData, false );
397}
398
399} // namespace KICAD_FORMAT::LEGACY
const char * name
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:94
COLOR4D GetTextColor() const
Definition eda_text.h:310
wxString GetFontName() const
bool IsItalic() const
Definition eda_text.h:200
bool IsMultilineAllowed() const
Definition eda_text.h:236
virtual bool IsVisible() const
Definition eda_text.h:226
virtual int GetTextHeight() const
Definition eda_text.h:307
KIFONT::FONT * GetFont() const
Definition eda_text.h:286
virtual bool HasHyperlink() const
Definition eda_text.h:448
wxString GetHyperlink() const
Definition eda_text.h:449
virtual int GetTextWidth() const
Definition eda_text.h:304
GR_TEXT_H_ALIGN_T GetHorizJustify() const
Definition eda_text.h:239
bool GetAutoThickness() const
Definition eda_text.h:170
double GetLineSpacing() const
Definition eda_text.h:298
bool IsMirrored() const
Definition eda_text.h:229
bool IsBold() const
Definition eda_text.h:215
GR_TEXT_V_ALIGN_T GetVertJustify() const
Definition eda_text.h:242
virtual int GetTextThickness() const
Definition eda_text.h:159
static RETURN_CODE DecompressAndDecode(EMBEDDED_FILE &aFile, bool aAllowEmptyHash=false)
Takes data from the compressedEncodedData buffer and Base64 decodes it.
static uint32_t Seed()
const std::map< wxString, std::shared_ptr< EMBEDDED_FILE > > & EmbeddedFileMap() const
Provide an iterable view of the file collection.
const wxString & GetName() const
Definition font.h:112
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:400
Definition kiid.h:46
wxString AsString() const
Definition kiid.cpp:264
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
const UTF8 & GetLibItemName() const
Definition lib_id.h:98
const UTF8 & GetLibNickname() const
Return the logical library name portion of a LIB_ID.
Definition lib_id.h:83
A streaming C++ equivalent for MurmurHash3_x64_128.
Definition mmh3_hash.h:56
FORCE_INLINE void addDataV1(const uint8_t *data, size_t length)
Definition mmh3_hash.h:95
FORCE_INLINE HASH_128 digest()
Definition mmh3_hash.h:136
An interface used to output 8 bit text in a convenient way.
Definition richio.h:294
std::string Quotew(const wxString &aWrapee) const
Definition richio.cpp:505
int PRINTF_FUNC_N Print(int nestLevel, const char *fmt,...)
Format and write text to the output stream.
Definition richio.cpp:432
Describe the page size and margins of a paper page on which to eventually print or plot.
Definition page_info.h:75
double GetHeightMils() const
Definition page_info.h:142
double GetWidthMils() const
Definition page_info.h:137
bool IsPortrait() const
Definition page_info.h:123
const PAGE_SIZE_TYPE & GetType() const
Definition page_info.h:97
Simple container to manage line stroke parameters.
int GetWidth() const
LINE_STYLE GetLineStyle() const
KIGFX::COLOR4D GetColor() const
Hold the information shown in the lower right corner of a plot, printout, or editing view.
Definition title_block.h:38
const wxString & GetCompany() const
Definition title_block.h:93
const wxString & GetRevision() const
Definition title_block.h:83
const wxString & GetDate() const
Definition title_block.h:73
const wxString & GetComment(int aIdx) const
const wxString & GetTitle() const
Definition title_block.h:60
An 8 bit string that is assuredly encoded in UTF8, and supplies special conversion support to and fro...
Definition utf8.h:67
bool empty() const
Definition utf8.h:105
static bool empty(const wxTextEntryBase *aCtrl)
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition idf_helpers.h:37
KICOMMON_API std::string FormatInternalUnits(const EDA_IU_SCALE &aIuScale, int aValue, EDA_DATA_TYPE aDataType=EDA_DATA_TYPE::DISTANCE)
Converts aValue from internal units to a string appropriate for writing to file.
const char * PageTypeToken(PAGE_SIZE_TYPE aType)
void FormatBool(OUTPUTFORMATTER *aOut, const wxString &aKey, bool aValue)
void FormatTitle(OUTPUTFORMATTER *aOut, const TITLE_BLOCK &aTitle)
void FormatOptBool(OUTPUTFORMATTER *aOut, const wxString &aKey, std::optional< bool > aValue)
void FormatText(OUTPUTFORMATTER *aOut, const EDA_TEXT &aText, const EDA_IU_SCALE &aScale, int aControlBits, bool aV9)
bool IsDefaultTextV10(const EDA_TEXT &aText)
void FormatStreamData(OUTPUTFORMATTER &aOut, const wxStreamBuffer &aStream)
void FormatEmbeddedFilesV9(OUTPUTFORMATTER &aOut, const EMBEDDED_FILES &aFiles, bool aWriteData)
void FormatUuid(OUTPUTFORMATTER *aOut, const KIID &aUuid)
const char * LineStyleToken(LINE_STYLE aStyle)
void FormatEmbeddedFiles(OUTPUTFORMATTER &aOut, const EMBEDDED_FILES &aFiles, bool aWriteData, bool aV9)
void FormatTextV9(OUTPUTFORMATTER *aOut, const EDA_TEXT &aText, const EDA_IU_SCALE &aScale, int aControlBits)
void FormatTextV10(OUTPUTFORMATTER *aOut, const EDA_TEXT &aText, const EDA_IU_SCALE &aScale, int aControlBits)
std::string EmbeddedFileChecksum(const EMBEDDED_FILES::EMBEDDED_FILE &aFile)
void FormatStroke(OUTPUTFORMATTER *aOut, const STROKE_PARAMS &aStroke, const EDA_IU_SCALE &aScale)
constexpr int OMIT_HIDE
UTF8 FormatLibId(const LIB_ID &aId)
void FormatPageV10(OUTPUTFORMATTER *aOut, const PAGE_INFO &aPage)
void FormatPageV9(OUTPUTFORMATTER *aOut, const PAGE_INFO &aPage)
void FormatEmbeddedFilesV10(OUTPUTFORMATTER &aOut, const EMBEDDED_FILES &aFiles, bool aWriteData)
int GetLegacyTextThickness(const EDA_TEXT &aText)
PAGE_SIZE_TYPE
Definition page_info.h:46
std::string FormatDouble2Str(double aValue)
Print a float number without using scientific notation and no trailing 0 This function is intended in...
LINE_STYLE
Dashed line types.
std::vector< char > decompressedData
std::string ToString() const
Definition hash_128.h:43
wxString result
Test unit parsing edge cases and error handling.
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_CENTER
@ GR_TEXT_V_ALIGN_TOP