KiCad PCB EDA Suite
Loading...
Searching...
No Matches
orcad_ole.cpp
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
13
14#include <algorithm>
15#include <array>
16#include <cmath>
17#include <cstring>
18#include <limits>
19#include <string_view>
20
21#include <wx/image.h>
22
23#include <compoundfilereader.h>
24#include <paths.h>
25
26#include <libwmf/api.h>
27#include <libwmf/gd.h>
28
29
30namespace
31{
32
33constexpr std::array<uint8_t, 8> CFB_MAGIC = { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 };
34
35
36uint32_t readU32( const uint8_t* aData )
37{
38 return static_cast<uint32_t>( aData[0] ) | ( static_cast<uint32_t>( aData[1] ) << 8 )
39 | ( static_cast<uint32_t>( aData[2] ) << 16 ) | ( static_cast<uint32_t>( aData[3] ) << 24 );
40}
41
42
43bool entryNameIs( const CFB::COMPOUND_FILE_ENTRY* aEntry, uint16_t aPrefix, std::string_view aName )
44{
45 size_t length = aEntry->nameLen >= 2 ? aEntry->nameLen / 2 - 1 : 0;
46
47 if( length != aName.size() + 1 || aEntry->name[0] != aPrefix )
48 return false;
49
50 for( size_t i = 0; i < aName.size(); ++i )
51 {
52 if( aEntry->name[i + 1] != static_cast<uint8_t>( aName[i] ) )
53 return false;
54 }
55
56 return true;
57}
58
59
60std::vector<uint8_t> readStream( const CFB::CompoundFileReader& aReader, const CFB::COMPOUND_FILE_ENTRY* aEntry )
61{
62 uint64_t size = aReader.GetStreamSize( aEntry );
63
64 if( size > aReader.GetBufferLen() || size > std::numeric_limits<size_t>::max() )
65 return {};
66
67 std::vector<uint8_t> data( static_cast<size_t>( size ) );
68
69 if( !data.empty() )
70 aReader.ReadFile( aEntry, 0, reinterpret_cast<char*>( data.data() ), data.size() );
71
72 return data;
73}
74
75} // namespace
76
77
78ORCAD_OLE_PREVIEW OrcadExtractOlePreview( const std::vector<uint8_t>& aPayload )
79{
80 auto cfb = std::search( aPayload.begin(), aPayload.end(), CFB_MAGIC.begin(), CFB_MAGIC.end() );
81
82 if( cfb == aPayload.end() )
83 return {};
84
85 try
86 {
87 const uint8_t* base = &*cfb;
88 size_t size = static_cast<size_t>( aPayload.end() - cfb );
89 CFB::CompoundFileReader reader( base, size );
90 const CFB::COMPOUND_FILE_ENTRY* presentation = nullptr;
91 const CFB::COMPOUND_FILE_ENTRY* native = nullptr;
92
93 reader.EnumFiles( reader.GetRootEntry(), -1,
94 [&]( const CFB::COMPOUND_FILE_ENTRY* aEntry, const CFB::utf16string&, int )
95 {
96 if( !reader.IsStream( aEntry ) )
97 return 0;
98
99 if( entryNameIs( aEntry, 2, "OlePres000" ) )
100 presentation = aEntry;
101 else if( entryNameIs( aEntry, 1, "Ole10Native" ) )
102 native = aEntry;
103
104 return 0;
105 } );
106
107 if( presentation )
108 {
109 std::vector<uint8_t> data = readStream( reader, presentation );
110
111 if( data.size() >= 40 )
112 {
113 uint32_t clipboardFormat = readU32( data.data() + 4 );
114
115 if( clipboardFormat == 3 )
116 return { ORCAD_OLE_PREVIEW_TYPE::WMF, { data.begin() + 40, data.end() } };
117
118 if( clipboardFormat == 8 )
119 return { ORCAD_OLE_PREVIEW_TYPE::DIB, { data.begin() + 40, data.end() } };
120
121 if( clipboardFormat == 14 )
122 return { ORCAD_OLE_PREVIEW_TYPE::WMF, { data.begin() + 40, data.end() } };
123 }
124 }
125
126 if( native )
127 {
128 std::vector<uint8_t> data = readStream( reader, native );
129 size_t limit = std::min<size_t>( data.size(), 64 );
130
131 for( size_t offset = 0; offset + 14 <= limit; ++offset )
132 {
133 if( data[offset] == 'B' && data[offset + 1] == 'M' )
134 return { ORCAD_OLE_PREVIEW_TYPE::BMP, { data.begin() + offset, data.end() } };
135 }
136 }
137 }
138 catch( const std::exception& )
139 {
140 }
141
142 return {};
143}
144
145
146bool OrcadRenderWmf( const std::vector<uint8_t>& aWmf, int aMaxWidth, int aMaxHeight, wxImage& aImage )
147{
148 if( aWmf.empty() || aWmf.size() > static_cast<size_t>( std::numeric_limits<long>::max() ) )
149 return false;
150
151 wmfAPI* api = nullptr;
152 wmfAPI_Options options{};
153 wxCharBuffer fontDir = ( PATHS::GetStockDataPath() + wxS( "/libwmf/fonts" ) ).utf8_str();
154 char* fontDirs[] = { fontDir.data(), nullptr };
155 options.function = wmf_gd_function;
156 options.fontdirs = fontDirs;
157
158 constexpr unsigned long flags =
159 WMF_OPT_FUNCTION | WMF_OPT_FONTDIRS | WMF_OPT_IGNORE_NONFATAL | WMF_OPT_NO_DEBUG | WMF_OPT_NO_ERROR;
160
161 if( wmf_api_create( &api, flags, &options ) != wmf_E_None )
162 return false;
163
164 auto destroyApi = [&]
165 {
166 wmf_api_destroy( api );
167 api = nullptr;
168 };
169
170 wmf_gd_t* gd = WMF_GD_GetData( api );
171 gd->type = wmf_gd_image;
172
173 if( wmf_mem_open( api, const_cast<uint8_t*>( aWmf.data() ), static_cast<long>( aWmf.size() ) ) != wmf_E_None )
174 {
175 destroyApi();
176 return false;
177 }
178
179 wmfD_Rect bbox;
180
181 if( wmf_scan( api, 0, &bbox ) != wmf_E_None )
182 {
183 destroyApi();
184 return false;
185 }
186
187 unsigned int naturalWidth = 0;
188 unsigned int naturalHeight = 0;
189
190 if( wmf_display_size( api, &naturalWidth, &naturalHeight, 144.0, 144.0 ) != wmf_E_None || naturalWidth == 0
191 || naturalHeight == 0 )
192 {
193 destroyApi();
194 return false;
195 }
196
197 double scale = std::min( static_cast<double>( std::max( 1, aMaxWidth ) ) / naturalWidth,
198 static_cast<double>( std::max( 1, aMaxHeight ) ) / naturalHeight );
199 scale = std::min( scale, 1.0 );
200 unsigned int width = static_cast<unsigned int>( std::max<long>( 1, std::lround( naturalWidth * scale ) ) );
201 unsigned int height = static_cast<unsigned int>( std::max<long>( 1, std::lround( naturalHeight * scale ) ) );
202
203 gd->bbox = bbox;
204 gd->width = width;
205 gd->height = height;
206
207 if( wmf_play( api, 0, &bbox ) != wmf_E_None )
208 {
209 destroyApi();
210 return false;
211 }
212
213 int* pixels = wmf_gd_get_image_pixels( api );
214
215 if( !pixels || !aImage.Create( width, height, false ) )
216 {
217 destroyApi();
218 return false;
219 }
220
221 unsigned char* rgb = aImage.GetData();
222
223 for( size_t i = 0; i < static_cast<size_t>( width ) * height; ++i )
224 {
225 rgb[3 * i] = static_cast<unsigned char>( ( pixels[i] >> 16 ) & 0xFF );
226 rgb[3 * i + 1] = static_cast<unsigned char>( ( pixels[i] >> 8 ) & 0xFF );
227 rgb[3 * i + 2] = static_cast<unsigned char>( pixels[i] & 0xFF );
228 }
229
230 destroyApi();
231 return true;
232}
static wxString GetStockDataPath(bool aRespectRunFromBuildDir=true)
Gets the stock (install) data path, which is the base path for things like scripting,...
Definition paths.cpp:233
ORCAD_OLE_PREVIEW OrcadExtractOlePreview(const std::vector< uint8_t > &aPayload)
Definition orcad_ole.cpp:78
bool OrcadRenderWmf(const std::vector< uint8_t > &aWmf, int aMaxWidth, int aMaxHeight, wxImage &aImage)
const int scale