/src/libstaroffice/src/lib/StarObjectModel.cxx
Line | Count | Source |
1 | | /* -*- Mode: C++; c-default-style: "k&r"; indent-tabs-mode: nil; tab-width: 2; c-basic-offset: 2 -*- */ |
2 | | |
3 | | /* libstaroffice |
4 | | * Version: MPL 2.0 / LGPLv2+ |
5 | | * |
6 | | * The contents of this file are subject to the Mozilla Public License Version |
7 | | * 2.0 (the "License"); you may not use this file except in compliance with |
8 | | * the License or as specified alternatively below. You may obtain a copy of |
9 | | * the License at http://www.mozilla.org/MPL/ |
10 | | * |
11 | | * Software distributed under the License is distributed on an "AS IS" basis, |
12 | | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
13 | | * for the specific language governing rights and limitations under the |
14 | | * License. |
15 | | * |
16 | | * Major Contributor(s): |
17 | | * Copyright (C) 2002 William Lachance (wrlach@gmail.com) |
18 | | * Copyright (C) 2002,2004 Marc Maurer (uwog@uwog.net) |
19 | | * Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch) |
20 | | * Copyright (C) 2006, 2007 Andrew Ziem |
21 | | * Copyright (C) 2011, 2012 Alonso Laurent (alonso@loria.fr) |
22 | | * |
23 | | * |
24 | | * All Rights Reserved. |
25 | | * |
26 | | * For minor contributions see the git repository. |
27 | | * |
28 | | * Alternatively, the contents of this file may be used under the terms of |
29 | | * the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"), |
30 | | * in which case the provisions of the LGPLv2+ are applicable |
31 | | * instead of those above. |
32 | | */ |
33 | | |
34 | | #include <cstring> |
35 | | #include <iomanip> |
36 | | #include <iostream> |
37 | | #include <limits> |
38 | | #include <set> |
39 | | #include <sstream> |
40 | | |
41 | | #include <librevenge/librevenge.h> |
42 | | |
43 | | #include "STOFFFrameStyle.hxx" |
44 | | #include "STOFFGraphicListener.hxx" |
45 | | #include "STOFFListener.hxx" |
46 | | #include "STOFFOLEParser.hxx" |
47 | | #include "STOFFPageSpan.hxx" |
48 | | |
49 | | #include "StarAttribute.hxx" |
50 | | #include "StarItemPool.hxx" |
51 | | #include "StarObject.hxx" |
52 | | #include "StarObjectSmallGraphic.hxx" |
53 | | #include "StarState.hxx" |
54 | | #include "StarZone.hxx" |
55 | | |
56 | | #include "StarObjectModel.hxx" |
57 | | |
58 | | /** Internal: the structures of a StarObjectModel */ |
59 | | namespace StarObjectModelInternal |
60 | | { |
61 | | //! small function used to convert a list of uint8_t to a bool vector |
62 | | static bool convertUint8ListToBoolList(std::vector<int> const &orig, std::vector<bool> &res) |
63 | 16.3k | { |
64 | 16.3k | size_t n=orig.size(); |
65 | 16.3k | res.resize(n*8); |
66 | 540k | for (size_t i=0, j=0; i<n; ++i) { |
67 | 524k | int val=orig[i]; |
68 | 4.71M | for (int k=0, depl=0x80; k<8; depl>>=1, ++k) |
69 | 4.19M | res[j++]=(val&depl); |
70 | 524k | } |
71 | 16.3k | return true; |
72 | 16.3k | } |
73 | | //////////////////////////////////////// |
74 | | //! Internal: class used to store a layer and its data |
75 | | class Layer |
76 | | { |
77 | | public: |
78 | | //! constructor |
79 | | Layer() |
80 | 203k | : m_name("") |
81 | 203k | , m_id(0) |
82 | 203k | , m_type(0) |
83 | 203k | { |
84 | 203k | } |
85 | | //! operator<< |
86 | | friend std::ostream &operator<<(std::ostream &o, Layer const &layer) |
87 | 0 | { |
88 | 0 | o << "id=" << layer.m_id << ","; |
89 | 0 | if (!layer.m_name.empty()) o << layer.m_name.cstr() << ","; |
90 | 0 | if (layer.m_type==0) o << "user,"; |
91 | 0 | return o; |
92 | 0 | } |
93 | | //! the layer name |
94 | | librevenge::RVNGString m_name; |
95 | | //! the layer id |
96 | | int m_id; |
97 | | //! the layer type |
98 | | int m_type; |
99 | | }; |
100 | | //////////////////////////////////////// |
101 | | //! Internal: class used to store a layer set and its data |
102 | | class LayerSet |
103 | | { |
104 | | public: |
105 | | //! constructor |
106 | | LayerSet() |
107 | 58.1k | : m_name("") |
108 | 58.1k | , m_memberList() |
109 | 58.1k | , m_excludeList() |
110 | 58.1k | { |
111 | 58.1k | } |
112 | | //! operator<< |
113 | | friend std::ostream &operator<<(std::ostream &o, LayerSet const &layer) |
114 | 0 | { |
115 | 0 | if (!layer.m_name.empty()) o << layer.m_name.cstr() << ","; |
116 | 0 | o << "members=["; |
117 | 0 | for (size_t i=0; i<layer.m_memberList.size(); ++i) { |
118 | 0 | if (layer.m_memberList[i]) o << i << ","; |
119 | 0 | } |
120 | 0 | o << "],"; |
121 | 0 | o << "excludes=["; |
122 | 0 | for (size_t i=0; i<layer.m_excludeList.size(); ++i) { |
123 | 0 | if (layer.m_excludeList[i]) o << i << ","; |
124 | 0 | } |
125 | 0 | o << "],"; |
126 | 0 | return o; |
127 | 0 | } |
128 | | //! the layer name |
129 | | librevenge::RVNGString m_name; |
130 | | //! the list of member |
131 | | std::vector<bool> m_memberList; |
132 | | //! the list of exclude |
133 | | std::vector<bool> m_excludeList; |
134 | | }; |
135 | | |
136 | | //////////////////////////////////////// |
137 | | //! Internal: class used to store a page and its data |
138 | | class Page |
139 | | { |
140 | | public: |
141 | | //////////////////////////////////////// |
142 | | //! Internal: class used to store a page descriptor |
143 | | struct Descriptor { |
144 | | //! constructor |
145 | | Descriptor() |
146 | 14.7k | : m_masterId(1) |
147 | 14.7k | , m_layerList() |
148 | 14.7k | { |
149 | 14.7k | } |
150 | | //! operator<< |
151 | | friend std::ostream &operator<<(std::ostream &o, Descriptor const &desc) |
152 | 0 | { |
153 | 0 | o << "id[master]=" << desc.m_masterId << ","; |
154 | 0 | o << "inVisLayer=["; |
155 | 0 | for (size_t i=0; i<desc.m_layerList.size(); ++i) { |
156 | 0 | if (!desc.m_layerList[i]) |
157 | 0 | o << i << ","; |
158 | 0 | } |
159 | 0 | o << "],"; |
160 | 0 | return o; |
161 | 0 | } |
162 | | //! the page number |
163 | | int m_masterId; |
164 | | //! a list of layer |
165 | | std::vector<bool> m_layerList; |
166 | | }; |
167 | | //! constructor |
168 | | Page() |
169 | 55.9k | : m_masterPage(false) |
170 | 55.9k | , m_name("") |
171 | 55.9k | , m_size(0,0) |
172 | 55.9k | , m_masterPageDescList() |
173 | 55.9k | , m_layer() |
174 | 55.9k | , m_layerSet() |
175 | 55.9k | , m_objectList() |
176 | 55.9k | , m_background() |
177 | 55.9k | { |
178 | 223k | for (int &border : m_borders) border=0; |
179 | 55.9k | } |
180 | | //! returns the master page id |
181 | | int getMasterPageId() const |
182 | 17.1k | { |
183 | 17.1k | if (m_masterPage || m_masterPageDescList.empty()) return -1; |
184 | 14.4k | return m_masterPageDescList[0].m_masterId; |
185 | 17.1k | } |
186 | | //! update pagespan properties |
187 | | void updatePageSpan(STOFFPageSpan &page, double relUnit) const |
188 | 14.0k | { |
189 | 14.0k | if (m_size[0]>0 && m_size[0]!=0x7fffffff) |
190 | 13.4k | page.m_propertiesList[0].insert("fo:page-width", relUnit*m_size[0], librevenge::RVNG_POINT); |
191 | 14.0k | if (m_size[1]>0 && m_size[1]!=0x7fffffff) |
192 | 13.2k | page.m_propertiesList[0].insert("fo:page-height", relUnit*m_size[1], librevenge::RVNG_POINT); |
193 | 70.1k | for (int i=0; i<4; ++i) { |
194 | 56.0k | if (m_borders[i]<0 || m_borders[i]==0x7fffffff) continue; |
195 | 53.7k | char const *wh[]= {"left", "top", "right", "bottom"}; |
196 | 53.7k | page.m_propertiesList[0].insert((std::string("fo:margin-")+wh[i]).c_str(), |
197 | 53.7k | relUnit*m_borders[i], librevenge::RVNG_POINT); |
198 | 53.7k | } |
199 | 14.0k | if (m_background) { |
200 | 1 | STOFF_DEBUG_MSG(("StarObjectModelInternal::Page::updatePageSpan: sorry sending background object is not implemented\n")); |
201 | 1 | } |
202 | 14.0k | } |
203 | | //! operator<< |
204 | | friend std::ostream &operator<<(std::ostream &o, Page const &page) |
205 | 0 | { |
206 | 0 | if (page.m_masterPage) o << "master,"; |
207 | 0 | if (!page.m_name.empty()) o << "name=" << page.m_name.cstr() << ","; |
208 | 0 | o << "sz=" << page.m_size << ","; |
209 | 0 | o << "borders=["; |
210 | 0 | for (int border : page.m_borders) o << border << ","; |
211 | 0 | o << "],"; |
212 | 0 | if (!page.m_masterPageDescList.empty()) { |
213 | 0 | o << "desc=["; |
214 | 0 | for (auto const &desc : page.m_masterPageDescList) |
215 | 0 | o << "[" << desc << "],"; |
216 | 0 | o << "],"; |
217 | 0 | } |
218 | 0 | o << "layer=[" << page.m_layer << "],"; |
219 | 0 | o << "layerSet=[" << page.m_layerSet << "],"; |
220 | 0 | if (page.m_background) o << "hasBackground,"; |
221 | 0 | #if 1 |
222 | 0 | for (auto &obj : page.m_objectList) { |
223 | 0 | if (obj) |
224 | 0 | o << "\n\t\t" << *obj; |
225 | 0 | } |
226 | 0 | o << "\n"; |
227 | | #else |
228 | | o << "num[object]=" << page.m_objectList.size() << ","; |
229 | | #endif |
230 | 0 | return o; |
231 | 0 | } |
232 | | //! a flag to know if the page is a master page |
233 | | bool m_masterPage; |
234 | | //! the page name |
235 | | librevenge::RVNGString m_name; |
236 | | //! the page size |
237 | | STOFFVec2i m_size; |
238 | | //! the border size: left, up, right, bottom |
239 | | int m_borders[4]; |
240 | | //! the list of master page descriptor |
241 | | std::vector<Descriptor> m_masterPageDescList; |
242 | | //! the layer |
243 | | Layer m_layer; |
244 | | //! the layer set |
245 | | LayerSet m_layerSet; |
246 | | //! the list of object |
247 | | std::vector<std::shared_ptr<StarObjectSmallGraphic> > m_objectList; |
248 | | //! the background object |
249 | | std::shared_ptr<StarObjectSmallGraphic> m_background; |
250 | | }; |
251 | | |
252 | | //////////////////////////////////////// |
253 | | //! Internal: the state of a StarObjectModel |
254 | | struct State { |
255 | | //! constructor |
256 | | State() |
257 | 49.6k | : m_previewMasterPage(-1) |
258 | 49.6k | , m_pageList() |
259 | 49.6k | , m_masterPageList() |
260 | 49.6k | , m_idToLayerMap() |
261 | 49.6k | , m_layerSetList() |
262 | 49.6k | , m_pageToSendList() |
263 | 49.6k | , m_masterPageToSendSet() |
264 | 49.6k | , m_idToObjectMap() |
265 | 49.6k | { |
266 | 49.6k | } |
267 | | //! small operator<< to print the content of the state |
268 | | friend std::ostream &operator<<(std::ostream &o, State const &state) |
269 | 0 | { |
270 | 0 | if (state.m_previewMasterPage>=0) |
271 | 0 | o << "prev[masterPage]=" << state.m_previewMasterPage << ","; |
272 | 0 | if (!state.m_pageList.empty()) { |
273 | 0 | o << "pages=[\n"; |
274 | 0 | for (auto &page : state.m_pageList) { |
275 | 0 | if (!page) |
276 | 0 | continue; |
277 | 0 | o << "\t" << *page << "\n"; |
278 | 0 | } |
279 | 0 | o << "]\n"; |
280 | 0 | } |
281 | 0 | if (!state.m_masterPageList.empty()) { |
282 | 0 | o << "masterPages=[\n"; |
283 | 0 | for (auto &page : state.m_masterPageList) { |
284 | 0 | if (!page) |
285 | 0 | continue; |
286 | 0 | o << "\t" << *page << "\n"; |
287 | 0 | } |
288 | 0 | o << "]\n"; |
289 | 0 | } |
290 | 0 | if (!state.m_idToLayerMap.empty()) { |
291 | 0 | o << "layers=["; |
292 | 0 | for (auto it : state.m_idToLayerMap) |
293 | 0 | o << "[" << it.second << "],"; |
294 | 0 | o << "]\n"; |
295 | 0 | } |
296 | 0 | if (!state.m_layerSetList.empty()) { |
297 | 0 | o << "layerSets=[\n"; |
298 | 0 | for (auto const &layer : state.m_layerSetList) |
299 | 0 | o << "\t" << layer << "\n"; |
300 | 0 | o << "]\n"; |
301 | 0 | } |
302 | 0 | return o; |
303 | 0 | } |
304 | | |
305 | | //! the default master page: -1 means not found, in general 1 in StarDrawDocument |
306 | | int m_previewMasterPage; |
307 | | /* |
308 | | in general: page0(master0), nothing |
309 | | after by pair: |
310 | | page 2*i+1(master 2*k+1): page content |
311 | | page 2*i+2(master 2*k+2): a page graphic+a text zone(double click to add note) |
312 | | */ |
313 | | //! list of pages |
314 | | std::vector<std::shared_ptr<Page> > m_pageList; |
315 | | /* |
316 | | in general: master0 contains 4 small box associated with a page graphic 1,3,5,7 |
317 | | after by pair: |
318 | | master 2*i+1: the content of the master page |
319 | | master 2*i+2: a title text+text: maybe default text |
320 | | */ |
321 | | //! list of master pages |
322 | | std::vector<std::shared_ptr<Page> > m_masterPageList; |
323 | | //! a map layerId to layer |
324 | | std::map<int, Layer> m_idToLayerMap; |
325 | | //! the list of layer set |
326 | | std::vector<LayerSet> m_layerSetList; |
327 | | //! the list of page to send |
328 | | std::vector<int> m_pageToSendList; |
329 | | //! the list of master page to send |
330 | | std::set<int> m_masterPageToSendSet; |
331 | | //! a map objectId to object |
332 | | std::map<int, std::shared_ptr<StarObjectSmallGraphic> > m_idToObjectMap; |
333 | | |
334 | | }; |
335 | | |
336 | | } |
337 | | |
338 | | //////////////////////////////////////////////////////////// |
339 | | // constructor/destructor, ... |
340 | | //////////////////////////////////////////////////////////// |
341 | | StarObjectModel::StarObjectModel(StarObject const &orig, bool duplicateState) |
342 | 49.6k | : StarObject(orig, duplicateState) |
343 | 49.6k | , m_modelState(new StarObjectModelInternal::State) |
344 | 49.6k | { |
345 | 49.6k | } |
346 | | |
347 | | StarObjectModel::~StarObjectModel() |
348 | 49.6k | { |
349 | 49.6k | } |
350 | | |
351 | | std::ostream &operator<<(std::ostream &o, StarObjectModel const &model) |
352 | 0 | { |
353 | 0 | o << *model.m_modelState; |
354 | 0 | return o; |
355 | 0 | } |
356 | | |
357 | | //////////////////////////////////////////////////////////// |
358 | | // send data |
359 | | //////////////////////////////////////////////////////////// |
360 | | bool StarObjectModel::updatePageSpans(std::vector<STOFFPageSpan> &pageSpan, int &number, bool usePage0) const |
361 | 13.2k | { |
362 | 13.2k | m_modelState->m_pageToSendList.clear(); |
363 | 13.2k | m_modelState->m_masterPageToSendSet.clear(); |
364 | | |
365 | 13.2k | auto pool=const_cast<StarObjectModel *>(this)->findItemPool(StarItemPool::T_XOutdevPool, false); |
366 | 13.2k | double const relUnit=pool ? pool->getRelativeUnit() : 0.028346457; |
367 | 13.2k | pageSpan.clear(); |
368 | 13.2k | if (usePage0) { |
369 | 2.45k | if (m_modelState->m_pageList.empty() || !m_modelState->m_pageList[0]) |
370 | 339 | return false; |
371 | 2.11k | STOFFPageSpan ps; |
372 | 2.11k | m_modelState->m_pageList[0]->updatePageSpan(ps, relUnit); |
373 | 2.11k | pageSpan.push_back(ps); |
374 | 2.11k | return true; |
375 | 2.45k | } |
376 | 10.8k | auto numMasterPage=int(m_modelState->m_masterPageList.size()); |
377 | 27.9k | for (size_t i=0; i<m_modelState->m_pageList.size(); ++i) { |
378 | 17.1k | if (!m_modelState->m_pageList[i]) |
379 | 0 | continue; |
380 | 17.1k | auto const &page=*m_modelState->m_pageList[i]; |
381 | 17.1k | int id=page.getMasterPageId(); |
382 | 17.1k | if (id<=0 || (id&1)!=1) continue; |
383 | 8.42k | m_modelState->m_pageToSendList.push_back(int(i)); |
384 | 8.42k | STOFFPageSpan ps; |
385 | 8.42k | page.updatePageSpan(ps, relUnit); |
386 | 8.42k | if (id<numMasterPage && m_modelState->m_masterPageList[size_t(id)]) { |
387 | 4.60k | m_modelState->m_masterPageToSendSet.insert(id); |
388 | 4.60k | librevenge::RVNGString masterName; |
389 | 4.60k | masterName.sprintf("Master%d", id); |
390 | 4.60k | ps.m_propertiesList[0].insert("librevenge:master-page-name", masterName); |
391 | 4.60k | } |
392 | 8.42k | pageSpan.push_back(ps); |
393 | 8.42k | } |
394 | 10.8k | number=int(m_modelState->m_pageToSendList.size()); |
395 | 10.8k | return number!=0; |
396 | 13.2k | } |
397 | | |
398 | | void StarObjectModel::updateObjectIds(std::set<long> &unusedId) |
399 | 3 | { |
400 | 3 | if (m_modelState->m_pageList.empty() || !m_modelState->m_pageList[0]) |
401 | 0 | return; |
402 | 3 | int actId=0; |
403 | 6 | for (auto &obj : m_modelState->m_pageList[0]->m_objectList) { |
404 | 6 | while (unusedId.find(long(actId))!=unusedId.end()) |
405 | 0 | ++actId; |
406 | 6 | if (obj) |
407 | 6 | m_modelState->m_idToObjectMap[actId]=obj; |
408 | 6 | ++actId; |
409 | 6 | } |
410 | 3 | } |
411 | | |
412 | | bool StarObjectModel::sendObject(int id, STOFFListenerPtr listener, StarState const &state) |
413 | 6 | { |
414 | 6 | auto it=m_modelState->m_idToObjectMap.find(id); |
415 | 6 | if (it==m_modelState->m_idToObjectMap.end() || !it->second) { |
416 | 0 | STOFF_DEBUG_MSG(("StarObjectModel::sendObject: can not find object %d\n", id)); |
417 | 0 | return false; |
418 | 0 | } |
419 | 6 | return it->second->send(listener, state.m_frame, *this); |
420 | 6 | } |
421 | | |
422 | | bool StarObjectModel::sendPages(STOFFListenerPtr listener) |
423 | 10.7k | { |
424 | 10.7k | if (!listener) { |
425 | 0 | STOFF_DEBUG_MSG(("StarObjectModel::sendPages: can not find the listener\n")); |
426 | 0 | return false; |
427 | 0 | } |
428 | 10.7k | bool first=true; |
429 | 10.7k | for (auto &page : m_modelState->m_pageToSendList) { |
430 | 8.41k | if (!first) |
431 | 1.60k | listener->insertBreak(STOFFListener::PageBreak); |
432 | 6.80k | else |
433 | 6.80k | first=false; |
434 | 8.41k | sendPage(page, listener, false); |
435 | 8.41k | } |
436 | 10.7k | return true; |
437 | 10.7k | } |
438 | | |
439 | | bool StarObjectModel::sendMasterPages(STOFFGraphicListenerPtr listener) |
440 | 10.2k | { |
441 | 10.2k | if (!listener) { |
442 | 0 | STOFF_DEBUG_MSG(("StarObjectModel::sendMasterPages: can not find the listener\n")); |
443 | 0 | return false; |
444 | 0 | } |
445 | 10.2k | auto pool=const_cast<StarObjectModel *>(this)->findItemPool(StarItemPool::T_XOutdevPool, false); |
446 | 10.2k | double const relUnit=pool ? pool->getRelativeUnit() : 0.028346457; |
447 | 10.2k | auto numMasters=int(m_modelState->m_masterPageList.size()); |
448 | 10.2k | for (auto id : m_modelState->m_masterPageToSendSet) { |
449 | 3.48k | if (id<0 || id>=numMasters || !m_modelState->m_masterPageList[size_t(id)]) { |
450 | 0 | STOFF_DEBUG_MSG(("StarObjectModel::sendMasterPages: can not find master page %d\n", id)); |
451 | 0 | continue; |
452 | 0 | } |
453 | 3.48k | auto const &page=*m_modelState->m_masterPageList[size_t(id)]; |
454 | 3.48k | STOFFPageSpan ps; |
455 | 3.48k | page.updatePageSpan(ps, relUnit); |
456 | 3.48k | librevenge::RVNGString masterName; |
457 | 3.48k | masterName.sprintf("Master%d", id); |
458 | 3.48k | ps.m_propertiesList[0].insert("librevenge:master-page-name", masterName); |
459 | 3.48k | listener->openMasterPage(ps); |
460 | 3.48k | sendPage(id, listener, true); |
461 | 3.48k | listener->closeMasterPage(); |
462 | 3.48k | } |
463 | 10.2k | return true; |
464 | 10.2k | } |
465 | | |
466 | | bool StarObjectModel::sendPage(int pageId, STOFFListenerPtr listener, bool masterPage) |
467 | 25.1k | { |
468 | 25.1k | if (!listener) { |
469 | 0 | STOFF_DEBUG_MSG(("StarObjectModel::sendPage: can not find the listener\n")); |
470 | 0 | return false; |
471 | 0 | } |
472 | 25.1k | std::vector<std::shared_ptr<StarObjectModelInternal::Page> > const &pageList= |
473 | 25.1k | masterPage ? m_modelState->m_masterPageList : m_modelState->m_pageList; |
474 | 25.1k | if (pageId<0) { |
475 | 0 | STOFF_DEBUG_MSG(("StarObjectModel::sendPage: can not find page %d\n", pageId)); |
476 | 0 | return false; |
477 | 0 | } |
478 | 25.1k | if (pageId>=int(pageList.size()) || !pageList[size_t(pageId)]) |
479 | 1.24k | return false; |
480 | 23.8k | StarObjectModelInternal::Page &page=*pageList[size_t(pageId)]; |
481 | 23.8k | STOFFFrameStyle frame; |
482 | 23.8k | frame.m_position.m_anchorTo=STOFFPosition::Page; |
483 | 23.8k | if (!masterPage) frame.m_position.m_propertyList.insert("text:anchor-page-number", pageId+1); |
484 | 76.9k | for (auto pag : page.m_objectList) { |
485 | 76.9k | if (pag) |
486 | 76.9k | pag->send(listener, frame, *this, masterPage); |
487 | 76.9k | } |
488 | 23.8k | return true; |
489 | 25.1k | } |
490 | | |
491 | | //////////////////////////////////////////////////////////// |
492 | | // the parser |
493 | | //////////////////////////////////////////////////////////// |
494 | | bool StarObjectModel::read(StarZone &zone) |
495 | 49.6k | { |
496 | 49.6k | STOFFInputStreamPtr input=zone.input(); |
497 | | // first check magic |
498 | 49.6k | std::string magic(""); |
499 | 49.6k | long pos=input->tell(); |
500 | 248k | for (int i=0; i<4; ++i) magic+=char(input->readULong(1)); |
501 | 49.6k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
502 | 49.6k | if (magic!="DrMd") return false; |
503 | | |
504 | 17.5k | libstoff::DebugFile &ascFile=zone.ascii(); |
505 | 17.5k | libstoff::DebugStream f; |
506 | 17.5k | f << "Entries(SdrModel)[" << zone.getRecordLevel() << "]:"; |
507 | | // svdmodel.cxx operator>>(..., SdrModel &) |
508 | 17.5k | if (!zone.openSDRHeader(magic)) { |
509 | 323 | input->seek(pos, librevenge::RVNG_SEEK_SET); |
510 | 323 | STOFF_DEBUG_MSG(("StarObjectModel::read: can not open the SDR Header\n")); |
511 | 323 | f << "###"; |
512 | 323 | ascFile.addPos(pos); |
513 | 323 | ascFile.addNote(f.str().c_str()); |
514 | 323 | return false; |
515 | 323 | } |
516 | 17.2k | int version=zone.getHeaderVersion(); |
517 | 17.2k | f << magic << ",nVers=" << version << ","; |
518 | | |
519 | 17.2k | if (magic!="DrMd" || version>17) { |
520 | 33 | STOFF_DEBUG_MSG(("StarObjectModel::read: unexpected magic data\n")); |
521 | 33 | f << "###"; |
522 | 33 | ascFile.addPos(pos); |
523 | 33 | ascFile.addNote(f.str().c_str()); |
524 | 33 | return false; |
525 | 33 | } |
526 | 17.1k | ascFile.addPos(pos); |
527 | 17.1k | ascFile.addNote(f.str().c_str()); |
528 | 17.1k | pos=input->tell(); |
529 | | |
530 | 17.1k | f.str(""); |
531 | 17.1k | f << "SdrModelA[" << zone.getRecordLevel() << "]:"; |
532 | | // SdrModel::ReadData |
533 | 17.1k | if (!zone.openRecord()) { // SdrModelA1 |
534 | 255 | STOFF_DEBUG_MSG(("StarObjectModel::read: can not open downCompat\n")); |
535 | 255 | f << "###"; |
536 | 255 | ascFile.addPos(pos); |
537 | 255 | ascFile.addNote(f.str().c_str()); |
538 | 255 | zone.closeSDRHeader("SdrModel"); |
539 | 255 | return true; |
540 | 255 | } |
541 | | |
542 | 16.9k | ascFile.addPos(pos); |
543 | 16.9k | ascFile.addNote(f.str().c_str()); |
544 | 16.9k | pos=input->tell(); |
545 | 16.9k | f.str(""); |
546 | 16.9k | f << "SdrModelA[" << zone.getRecordLevel() << "]:"; |
547 | 16.9k | bool ok=true; |
548 | 16.9k | if (version>=11) { |
549 | 16.5k | magic=""; |
550 | 82.6k | for (int i=0; i<4; ++i) magic+=char(input->readULong(1)); |
551 | 16.5k | f << "joeM"; |
552 | 16.5k | if (magic!="JoeM" || !zone.openRecord()) { // SdrModelA2 |
553 | 86 | STOFF_DEBUG_MSG(("StarObjectModel::read: unexpected magic2 data\n")); |
554 | 86 | f << "###magic" << magic; |
555 | 86 | ascFile.addPos(pos); |
556 | 86 | ascFile.addNote(f.str().c_str()); |
557 | 86 | zone.closeRecord("SdrModelA1"); |
558 | 86 | zone.closeSDRHeader("SdrModel"); |
559 | 86 | return true; |
560 | 86 | } |
561 | 16.4k | ascFile.addPos(pos); |
562 | 16.4k | ascFile.addNote(f.str().c_str()); |
563 | | |
564 | 16.4k | pos=input->tell(); |
565 | 16.4k | f.str(""); |
566 | 16.4k | f << "SdrModelInfo[" << zone.getRecordLevel() << "]:"; |
567 | 16.4k | if (!zone.openRecord()) { // SdrModelInfo |
568 | 1.32k | STOFF_DEBUG_MSG(("StarObjectModel::read: can not read model info\n")); |
569 | 1.32k | f << "###"; |
570 | 1.32k | ok=false; |
571 | 1.32k | } |
572 | 15.1k | else { |
573 | | /* operator>>(..., SdrModelInfo&) |
574 | | |
575 | | normally, this document also contains a SfxDocumentInfo, so it is safe to ignore them |
576 | | */ |
577 | 15.1k | uint32_t date, time; |
578 | 15.1k | uint8_t enc; |
579 | 15.1k | *input >> date >> time >> enc; |
580 | 15.1k | f << "date=" << date << ",time=" << time << ",encoding=" << int(enc) << ","; |
581 | 15.1k | input->seek(3, librevenge::RVNG_SEEK_CUR); // creationGUI, creationCPU, creationSys |
582 | 15.1k | *input >> date >> time >> enc; |
583 | 15.1k | f << "modDate=" << date << ", modTime=" << time << ",encoding[mod]=" << int(enc) << ","; |
584 | 15.1k | input->seek(3, librevenge::RVNG_SEEK_CUR); // writeGUI, writeCPU, writeSys |
585 | 15.1k | *input >> date >> time >> enc; |
586 | 15.1k | f << "lastReadDate=" << date << ", lastReadTime=" << time << ",encoding[lastRead]=" << int(enc) << ","; |
587 | 15.1k | input->seek(3, librevenge::RVNG_SEEK_CUR); // lastReadGUI, lastReadCPU, lastReadSys |
588 | 15.1k | *input >> date >> time; |
589 | 15.1k | f << "lastPrintDate=" << date << ", lastPrintTime=" << time << ","; |
590 | 15.1k | ascFile.addPos(pos); |
591 | 15.1k | ascFile.addNote(f.str().c_str()); |
592 | | |
593 | 15.1k | zone.closeRecord("SdrModelInfo"); |
594 | 15.1k | pos=input->tell(); |
595 | 15.1k | f.str(""); |
596 | 15.1k | f << "SdrModelStats[" << zone.getRecordLevel() << "]:"; |
597 | 15.1k | if (!zone.openRecord()) { |
598 | 4.19k | STOFF_DEBUG_MSG(("StarObjectModel::read: can not read model statistic\n")); |
599 | 4.19k | f << "###"; |
600 | 4.19k | ok=false; |
601 | 4.19k | } |
602 | 10.9k | else { |
603 | 10.9k | zone.closeRecord("SdrModelStat"); |
604 | 10.9k | ascFile.addPos(pos); |
605 | 10.9k | ascFile.addNote(f.str().c_str()); |
606 | | |
607 | 10.9k | pos=input->tell(); |
608 | 10.9k | f.str(""); |
609 | 10.9k | f << "SdrModelFormatCompat[" << zone.getRecordLevel() << "]:"; |
610 | 10.9k | } |
611 | 15.1k | } |
612 | 16.4k | if (ok&&!zone.openRecord()) { |
613 | 457 | STOFF_DEBUG_MSG(("StarObjectModel::read: can not read model format compat\n")); |
614 | 457 | f << "###"; |
615 | 457 | ok=false; |
616 | 457 | } |
617 | 15.9k | else if (ok) { |
618 | 10.4k | if (input->tell()+6<=zone.getRecordLastPosition()) { |
619 | 1.58k | int16_t nTmp; // find 0 |
620 | 1.58k | *input >> nTmp; |
621 | 1.58k | if (nTmp) f << "unk=" << nTmp << ","; |
622 | 1.58k | } |
623 | 10.4k | if (input->tell()+4<=zone.getRecordLastPosition()) { |
624 | 10.4k | auto compressedMode=int(input->readULong(2)); |
625 | 10.4k | if (compressedMode) // checkme: find 0 or 16-17, do not seems to cause problem |
626 | 891 | f << "#streamCompressed=" << compressedMode << ","; |
627 | 10.4k | f << "nStreamNumberFormat=" << input->readULong(2) << ","; |
628 | 10.4k | } |
629 | 10.4k | zone.closeRecord("SdrModelFormatCompat"); |
630 | 10.4k | } |
631 | | |
632 | 16.4k | ascFile.addPos(pos); |
633 | 16.4k | ascFile.addNote(f.str().c_str()); |
634 | 16.4k | pos=input->tell(); |
635 | 16.4k | f.str(""); |
636 | 16.4k | f << "SdrModelA[" << zone.getRecordLevel() << "]-data:"; |
637 | 16.4k | } |
638 | 16.8k | if (ok) { |
639 | 10.8k | int32_t nNum, nDen; |
640 | 10.8k | uint16_t nTmp; |
641 | 10.8k | uint8_t nTmp8; |
642 | 10.8k | *input>> nNum >> nDen >> nTmp; |
643 | 10.8k | f << "objectUnit=" << nNum << "/" << nDen << ","; |
644 | 10.8k | if (nTmp) f << "eObjUnit=" << nTmp << ","; |
645 | 10.8k | input->seek(2, librevenge::RVNG_SEEK_CUR); // collapsed? |
646 | 10.8k | *input>>nTmp8; |
647 | 10.8k | if (nTmp8==1) f << "pageNotValid,"; |
648 | 10.8k | input->seek(1, librevenge::RVNG_SEEK_CUR); // dummy |
649 | | |
650 | 10.8k | if (version<1) { |
651 | 9 | STOFF_DEBUG_MSG(("StarObjectModel::read: reading model format for version 0 is not implemented\n")); |
652 | 9 | f << "###version0"; |
653 | 9 | ok=false; |
654 | 9 | } |
655 | 10.8k | else { |
656 | 10.8k | if (version<11) { |
657 | 402 | auto charSet=int(input->readLong(2)); |
658 | 402 | if (StarEncoding::getEncodingForId(charSet)!=StarEncoding::E_DONTKNOW) |
659 | 402 | zone.setEncoding(StarEncoding::getEncodingForId(charSet)); |
660 | 0 | else |
661 | 0 | f << "###nCharSet=" << charSet << ","; |
662 | 402 | } |
663 | 10.8k | std::vector<uint32_t> string; |
664 | 69.3k | for (int i=0; i<6; ++i) { |
665 | 60.3k | if (!zone.readString(string)) { |
666 | 1.84k | STOFF_DEBUG_MSG(("StarObjectModel::read: can not read a string\n")); |
667 | 1.84k | f << "###string"; |
668 | 1.84k | ok=false; |
669 | 1.84k | break; |
670 | 1.84k | } |
671 | 58.4k | if (string.empty()) continue; |
672 | 2.05k | static char const* const wh[]= {"cTableName", "dashName", "lineEndName", "hashName", "gradientName", "bitmapName"}; |
673 | 2.05k | f << wh[i] << "=" << libstoff::getString(string).cstr() << ","; |
674 | 2.05k | } |
675 | 10.8k | } |
676 | 10.8k | } |
677 | 16.8k | if (ok && version>=12 && input->tell()<zone.getRecordLastPosition()) { |
678 | 4.70k | int32_t nNum, nDen; |
679 | 4.70k | uint16_t nTmp; |
680 | 4.70k | *input>> nNum >> nDen >> nTmp; |
681 | 4.70k | f << "UIUnit=" << nNum << "/" << nDen << ","; |
682 | 4.70k | if (nTmp) f << "eUIUnit=" << nTmp << ","; |
683 | 4.70k | } |
684 | 16.8k | if (ok && version>=13 && input->tell()<zone.getRecordLastPosition()) { |
685 | 2.84k | int32_t nNum1, nNum2; |
686 | 2.84k | *input>> nNum1 >> nNum2; |
687 | 2.84k | if (nNum1) f << "nDefTextHgt=" << nNum1 << ","; |
688 | 2.84k | if (nNum2) f << "nDefTab=" << nNum2 << ","; |
689 | 2.84k | } |
690 | 16.8k | if (ok && version>=14 && input->tell()<zone.getRecordLastPosition()) { |
691 | 2.84k | uint16_t pNum; |
692 | 2.84k | *input>> pNum; |
693 | 2.84k | if (pNum!=0xFFFF) { |
694 | 2.20k | m_modelState->m_previewMasterPage=int(pNum); |
695 | 2.20k | f << "nStartPageNum=" << pNum << ","; |
696 | 2.20k | } |
697 | 2.84k | } |
698 | 16.8k | ascFile.addPos(pos); |
699 | 16.8k | ascFile.addNote(f.str().c_str()); |
700 | 16.8k | if (version>=11) { |
701 | 16.4k | zone.closeRecord("SdrModelA2"); |
702 | 16.4k | ok=true; |
703 | 16.4k | } |
704 | 134k | while (ok) { |
705 | 134k | pos=input->tell(); |
706 | 134k | if (pos+4>zone.getRecordLastPosition()) |
707 | 6.70k | break; |
708 | 127k | magic=""; |
709 | 637k | for (int i=0; i<4; ++i) magic+=char(input->readULong(1)); |
710 | 127k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
711 | 127k | if (magic=="DrLy") { |
712 | 50.9k | StarObjectModelInternal::Layer layer; |
713 | 50.9k | if (readSdrLayer(zone, layer)) { |
714 | 50.7k | if (m_modelState->m_idToLayerMap.find(layer.m_id)!=m_modelState->m_idToLayerMap.end()) { |
715 | 5.94k | STOFF_DEBUG_MSG(("StarObjectModel::read: layer %d already exists\n", layer.m_id)); |
716 | 5.94k | } |
717 | 44.8k | else |
718 | 44.8k | m_modelState->m_idToLayerMap[layer.m_id]=layer; |
719 | 50.7k | continue; |
720 | 50.7k | } |
721 | 50.9k | } |
722 | 76.7k | if (magic=="DrLS") { |
723 | 988 | StarObjectModelInternal::LayerSet layers; |
724 | 988 | if (readSdrLayerSet(zone, layers)) { |
725 | 943 | m_modelState->m_layerSetList.push_back(layers); |
726 | 943 | continue; |
727 | 943 | } |
728 | 988 | } |
729 | 75.8k | if ((magic=="DrPg" || magic=="DrMP")) { |
730 | 57.3k | auto page=readSdrPage(zone); |
731 | 57.3k | if (page) { |
732 | 47.3k | if (magic=="DrPg") |
733 | 29.4k | m_modelState->m_pageList.push_back(page); |
734 | 17.8k | else |
735 | 17.8k | m_modelState->m_masterPageList.push_back(page); |
736 | 47.3k | continue; |
737 | 47.3k | } |
738 | 57.3k | } |
739 | | |
740 | 28.5k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
741 | 28.5k | if (!zone.openSDRHeader(magic)) { |
742 | 9.74k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
743 | 9.74k | break; |
744 | 9.74k | } |
745 | 18.7k | f.str(""); |
746 | 18.7k | f << "SdrModel[" << magic << "-" << zone.getRecordLevel() << "]:"; |
747 | 18.7k | if (magic!="DrXX") { |
748 | 12.4k | STOFF_DEBUG_MSG(("StarObjectModel::read: find unexpected child\n")); |
749 | 12.4k | f << "###type"; |
750 | 12.4k | input->seek(zone.getRecordLastPosition(), librevenge::RVNG_SEEK_SET); |
751 | 12.4k | } |
752 | 18.7k | ascFile.addPos(pos); |
753 | 18.7k | ascFile.addNote(f.str().c_str()); |
754 | 18.7k | zone.closeSDRHeader("SdrModel"); |
755 | 18.7k | } |
756 | | |
757 | 16.8k | zone.closeRecord("SdrModelA1"); |
758 | | // in DrawingLayer, find also 0500000001 and 060000000[01]00 |
759 | 16.8k | pos=input->tell(); |
760 | 16.8k | if (pos+4<=zone.getRecordLastPosition()) { |
761 | 4.14k | auto sz=long(input->readULong(4)); |
762 | 4.14k | if (pos+sz==zone.getRecordLastPosition()) { |
763 | 1.71k | ascFile.addPos(pos); |
764 | 1.71k | ascFile.addNote("SdrModel:#extra"); |
765 | 1.71k | input->seek(zone.getRecordLastPosition(), librevenge::RVNG_SEEK_SET); |
766 | 1.71k | } |
767 | 2.42k | else |
768 | 2.42k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
769 | 4.14k | } |
770 | 16.8k | zone.closeSDRHeader("SdrModel"); |
771 | 16.8k | return true; |
772 | 16.9k | } |
773 | | |
774 | | bool StarObjectModel::readSdrLayer(StarZone &zone, StarObjectModelInternal::Layer &layer) |
775 | 51.7k | { |
776 | 51.7k | layer=StarObjectModelInternal::Layer(); |
777 | 51.7k | STOFFInputStreamPtr input=zone.input(); |
778 | | // first check magic |
779 | 51.7k | std::string magic(""); |
780 | 51.7k | long pos=input->tell(); |
781 | 258k | for (int i=0; i<4; ++i) magic+=char(input->readULong(1)); |
782 | 51.7k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
783 | 51.7k | if (magic!="DrLy") return false; |
784 | | |
785 | 51.7k | libstoff::DebugFile &ascFile=zone.ascii(); |
786 | 51.7k | libstoff::DebugStream f; |
787 | 51.7k | f << "Entries(SdrLayerDef)[" << zone.getRecordLevel() << "]:"; |
788 | | // svdlayer.cxx operator>>(..., SdrLayer &) |
789 | 51.7k | if (!zone.openSDRHeader(magic)) { |
790 | 275 | input->seek(pos, librevenge::RVNG_SEEK_SET); |
791 | 275 | STOFF_DEBUG_MSG(("StarObjectModel::readSdrLayer: can not open the SDR Header\n")); |
792 | 275 | f << "###"; |
793 | 275 | ascFile.addPos(pos); |
794 | 275 | ascFile.addNote(f.str().c_str()); |
795 | 275 | return false; |
796 | 275 | } |
797 | 51.4k | int version=zone.getHeaderVersion(); |
798 | 51.4k | f << magic << ",nVers=" << version << ","; |
799 | | |
800 | 51.4k | if (magic!="DrLy") { |
801 | 0 | STOFF_DEBUG_MSG(("StarObjectModel::readSdrLayer: unexpected magic data\n")); |
802 | 0 | f << "###"; |
803 | 0 | ascFile.addPos(pos); |
804 | 0 | ascFile.addNote(f.str().c_str()); |
805 | 0 | return false; |
806 | 0 | } |
807 | 51.4k | layer.m_id=int(input->readULong(1)); |
808 | 51.4k | std::vector<uint32_t> string; |
809 | 51.4k | if (!zone.readString(string)) { |
810 | 1.98k | STOFF_DEBUG_MSG(("StarObjectModel::readSdrLayer: can not read a string\n")); |
811 | 1.98k | f << "###string"; |
812 | 1.98k | ascFile.addPos(pos); |
813 | 1.98k | ascFile.addNote(f.str().c_str()); |
814 | 1.98k | zone.closeSDRHeader("SdrLayerDef"); |
815 | 1.98k | return true; |
816 | 1.98k | } |
817 | 49.5k | layer.m_name=libstoff::getString(string); |
818 | 49.5k | if (version>=1) layer.m_type=int(input->readULong(2)); |
819 | 49.5k | f << layer; |
820 | 49.5k | ascFile.addPos(pos); |
821 | 49.5k | ascFile.addNote(f.str().c_str()); |
822 | 49.5k | zone.closeSDRHeader("SdrLayerDef"); |
823 | 49.5k | return true; |
824 | 51.4k | } |
825 | | |
826 | | bool StarObjectModel::readSdrLayerSet(StarZone &zone, StarObjectModelInternal::LayerSet &layers) |
827 | 1.21k | { |
828 | 1.21k | layers=StarObjectModelInternal::LayerSet(); |
829 | 1.21k | STOFFInputStreamPtr input=zone.input(); |
830 | | // first check magic |
831 | 1.21k | std::string magic(""); |
832 | 1.21k | long pos=input->tell(); |
833 | 6.09k | for (int i=0; i<4; ++i) magic+=char(input->readULong(1)); |
834 | 1.21k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
835 | 1.21k | if (magic!="DrLS") return false; |
836 | | |
837 | 1.21k | libstoff::DebugFile &ascFile=zone.ascii(); |
838 | 1.21k | libstoff::DebugStream f; |
839 | 1.21k | f << "Entries(SdrLayerSet)[" << zone.getRecordLevel() << "]:"; |
840 | | // svdlayer.cxx operator>>(..., SdrLayerSet &) |
841 | 1.21k | if (!zone.openSDRHeader(magic)) { |
842 | 158 | input->seek(pos, librevenge::RVNG_SEEK_SET); |
843 | 158 | STOFF_DEBUG_MSG(("StarObjectModel::readSdrLayerSet: can not open the SDR Header\n")); |
844 | 158 | f << "###"; |
845 | 158 | ascFile.addPos(pos); |
846 | 158 | ascFile.addNote(f.str().c_str()); |
847 | 158 | return false; |
848 | 158 | } |
849 | 1.06k | int version=zone.getHeaderVersion(); |
850 | 1.06k | f << magic << ",nVers=" << version << ","; |
851 | | |
852 | 1.06k | if (magic!="DrLS") { |
853 | 0 | STOFF_DEBUG_MSG(("StarObjectModel::readSdrLayerSet: unexpected magic data\n")); |
854 | 0 | f << "###"; |
855 | 0 | ascFile.addPos(pos); |
856 | 0 | ascFile.addNote(f.str().c_str()); |
857 | 0 | return false; |
858 | 0 | } |
859 | 3.18k | for (int i=0; i<2; ++i) { |
860 | 2.12k | std::vector<int> layerList; |
861 | 69.9k | for (int j=0; j<32; ++j) layerList.push_back(int(input->readULong(1))); |
862 | 2.12k | StarObjectModelInternal::convertUint8ListToBoolList(layerList, i==0 ? layers.m_memberList : layers.m_excludeList); |
863 | 2.12k | } |
864 | 1.06k | std::vector<uint32_t> string; |
865 | 1.06k | if (!zone.readString(string)) { |
866 | 346 | STOFF_DEBUG_MSG(("StarObjectModel::readSdrLayerSet: can not read a string\n")); |
867 | 346 | f << layers << "###string"; |
868 | 346 | ascFile.addPos(pos); |
869 | 346 | ascFile.addNote(f.str().c_str()); |
870 | 346 | zone.closeSDRHeader("SdrLayerSet"); |
871 | 346 | return true; |
872 | 346 | } |
873 | 714 | layers.m_name=libstoff::getString(string).cstr(); |
874 | 714 | f << layers; |
875 | 714 | ascFile.addPos(pos); |
876 | 714 | ascFile.addNote(f.str().c_str()); |
877 | 714 | zone.closeSDRHeader("SdrLayerSet"); |
878 | 714 | return true; |
879 | 1.06k | } |
880 | | |
881 | | std::shared_ptr<StarObjectModelInternal::Page> StarObjectModel::readSdrPage(StarZone &zone) |
882 | 57.3k | { |
883 | 57.3k | STOFFInputStreamPtr input=zone.input(); |
884 | | // first check magic |
885 | 57.3k | std::string magic(""); |
886 | 57.3k | long pos=input->tell(); |
887 | 286k | for (int i=0; i<4; ++i) magic+=char(input->readULong(1)); |
888 | 57.3k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
889 | 57.3k | if (magic!="DrPg" && magic!="DrMP") return std::shared_ptr<StarObjectModelInternal::Page>(); |
890 | | |
891 | | // svdpage.cxx operator>> |
892 | 57.3k | libstoff::DebugFile &ascFile=zone.ascii(); |
893 | 57.3k | libstoff::DebugStream f; |
894 | 57.3k | f << "Entries(SdrPageDef)[" << zone.getRecordLevel() << "]:"; |
895 | 57.3k | if (magic=="DrMP") f << "master,"; |
896 | 57.3k | if (!zone.openSDRHeader(magic)) { |
897 | 1.35k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
898 | 1.35k | STOFF_DEBUG_MSG(("StarObjectModel::readSdrPage: can not open the SDR Header\n")); |
899 | 1.35k | f << "###"; |
900 | 1.35k | ascFile.addPos(pos); |
901 | 1.35k | ascFile.addNote(f.str().c_str()); |
902 | 1.35k | return std::shared_ptr<StarObjectModelInternal::Page>(); |
903 | 1.35k | } |
904 | 55.9k | int version=zone.getHeaderVersion(); |
905 | 55.9k | f << magic << ",nVers=" << version << ","; |
906 | | |
907 | 55.9k | if (magic!="DrPg" && magic!="DrMP") { |
908 | 0 | input->seek(pos, librevenge::RVNG_SEEK_SET); |
909 | 0 | STOFF_DEBUG_MSG(("StarObjectModel::readSdrPage: unexpected magic data\n")); |
910 | 0 | f << "###"; |
911 | 0 | ascFile.addPos(pos); |
912 | 0 | ascFile.addNote(f.str().c_str()); |
913 | 0 | return std::shared_ptr<StarObjectModelInternal::Page>(); |
914 | 0 | } |
915 | 55.9k | ascFile.addPos(pos); |
916 | 55.9k | ascFile.addNote(f.str().c_str()); |
917 | 55.9k | pos=input->tell(); |
918 | | |
919 | 55.9k | std::shared_ptr<StarObjectModelInternal::Page> page(new StarObjectModelInternal::Page); |
920 | 55.9k | page->m_masterPage=magic=="DrMP"; |
921 | 55.9k | f.str(""); |
922 | 55.9k | f << "SdrPageDefA[" << zone.getRecordLevel() << "]:"; |
923 | | // svdpage.cxx SdrPage::ReadData |
924 | 55.9k | bool hasExtraData=false; |
925 | 106k | for (int tr=0; tr<2; ++tr) { |
926 | | /* sometimes there is one or two block which appears before the |
927 | | SdrPage::ReadData data, so try to find them*/ |
928 | 106k | long lastPos=zone.getRecordLastPosition(); |
929 | 106k | if (!zone.openRecord()) { // SdrPageDefA1 |
930 | 5.83k | STOFF_DEBUG_MSG(("StarObjectModel::readSdrPage: can not open downCompat\n")); |
931 | 5.83k | f << "###"; |
932 | 5.83k | ascFile.addPos(pos); |
933 | 5.83k | ascFile.addNote(f.str().c_str()); |
934 | 5.83k | zone.closeSDRHeader("SdrPageDef"); |
935 | 5.83k | return std::shared_ptr<StarObjectModelInternal::Page>(); |
936 | 5.83k | } |
937 | 101k | if (tr==1 || zone.getRecordLastPosition()==lastPos) |
938 | 50.1k | break; |
939 | | // unsure, how we can have some dim and pool here but seems frequent in DrawingLayer |
940 | 50.8k | uint16_t n; |
941 | 50.8k | *input >> n; |
942 | 50.8k | bool ok=false; |
943 | 50.8k | if (n>1) { |
944 | 2.00k | STOFF_DEBUG_MSG(("StarObjectModel::readSdrPage: bad version\n")); |
945 | 2.00k | f << "###n=" << n << ","; |
946 | 2.00k | ok=false; |
947 | 2.00k | } |
948 | 48.8k | else |
949 | 48.8k | hasExtraData=true; |
950 | 50.8k | if (n==1 && input->tell()+8<=zone.getRecordLastPosition()) { |
951 | 15.7k | f << "dim=["; |
952 | 78.7k | for (int i=0; i<4; ++i) f << input->readLong(2) << ((i%2) ? "," : "x"); |
953 | 15.7k | f << "],"; |
954 | 15.7k | *input >> n; |
955 | 15.7k | ok=true; |
956 | 15.7k | } |
957 | 50.8k | if (ok && n>1 && input->tell()+2<=zone.getRecordLastPosition()) { |
958 | 549 | STOFF_DEBUG_MSG(("StarObjectModel::readSdrPage: bad version2\n")); |
959 | 549 | f << "###n2=" << n << ","; |
960 | 549 | ok=false; |
961 | 549 | } |
962 | 50.8k | if (ok && n==1) { |
963 | 14.4k | long actPos=input->tell(); |
964 | 14.4k | auto pool=getNewItemPool(StarItemPool::T_VCControlPool); |
965 | 14.4k | if (!pool || !pool->read(zone)) |
966 | 2.79k | input->seek(actPos, librevenge::RVNG_SEEK_SET); |
967 | 14.4k | } |
968 | 50.8k | ascFile.addPos(pos); |
969 | 50.8k | ascFile.addNote(f.str().c_str()); |
970 | | |
971 | 50.8k | zone.closeRecord("SdrPageDef"); |
972 | 50.8k | pos=input->tell(); |
973 | 50.8k | f.str(""); |
974 | 50.8k | f << "SdrPageDefA[" << zone.getRecordLevel() << "]:"; |
975 | 50.8k | } |
976 | 50.1k | ascFile.addPos(pos); |
977 | 50.1k | ascFile.addNote(f.str().c_str()); |
978 | 50.1k | pos=input->tell(); |
979 | 50.1k | f.str(""); |
980 | 50.1k | f << "SdrPageDefA[" << zone.getRecordLevel() << "]:"; |
981 | 50.1k | if (version>=11) { |
982 | 49.4k | magic=""; |
983 | 247k | for (int i=0; i<4; ++i) magic+=char(input->readULong(1)); |
984 | 49.4k | f << "joeM"; |
985 | 49.4k | if (magic!="JoeM" || !zone.openRecord()) { // SdrPageDefA2 |
986 | 2.79k | STOFF_DEBUG_MSG(("StarObjectModel::readSdrPage: unexpected magic2 data\n")); |
987 | 2.79k | f << "###magic" << magic; |
988 | 2.79k | ascFile.addPos(pos); |
989 | 2.79k | ascFile.addNote(f.str().c_str()); |
990 | 2.79k | zone.closeRecord("SdrPageDefA1"); |
991 | 2.79k | zone.closeSDRHeader("SdrPageDef"); |
992 | 2.79k | return std::shared_ptr<StarObjectModelInternal::Page>(); |
993 | 2.79k | } |
994 | 46.6k | ascFile.addPos(pos); |
995 | 46.6k | ascFile.addNote(f.str().c_str()); |
996 | | |
997 | 46.6k | pos=input->tell(); |
998 | 46.6k | f.str(""); |
999 | 46.6k | f << "SdrPageDefData[" << zone.getRecordLevel() << "]:"; |
1000 | 46.6k | } |
1001 | 47.3k | int32_t nWdt, nHgt; |
1002 | 47.3k | int32_t borders[4]; |
1003 | 47.3k | uint16_t n; |
1004 | 47.3k | *input >> nWdt >> nHgt >> borders[0] >> borders[1] >> borders[2] >> borders[3] >> n; |
1005 | 47.3k | f << "dim=" << nWdt << "x" << nHgt << ","; |
1006 | 47.3k | f << "border=[" << borders[0] << "x" << borders[1] << "-" << borders[2] << "x" << borders[3] << "],"; |
1007 | 47.3k | page->m_size=STOFFVec2i(int(nWdt), int(nHgt)); |
1008 | 236k | for (int i=0; i<4; ++i) page->m_borders[i]=int(borders[i]); |
1009 | 47.3k | if (n) f << "n=" << n << ","; // link to page name? |
1010 | 47.3k | ascFile.addPos(pos); |
1011 | 47.3k | ascFile.addNote(f.str().c_str()); |
1012 | 47.3k | if (version>=11) |
1013 | 46.6k | zone.closeRecord("SdrPageDefData"); |
1014 | | |
1015 | 47.3k | long lastPos=zone.getRecordLastPosition(); |
1016 | 47.3k | bool ok=true; |
1017 | 90.9k | while (ok) { |
1018 | 90.9k | pos=input->tell(); |
1019 | 90.9k | if (pos+4>lastPos) |
1020 | 203 | break; |
1021 | 90.7k | magic=""; |
1022 | 453k | for (int i=0; i<4; ++i) magic+=char(input->readULong(1)); |
1023 | 90.7k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
1024 | 90.7k | if (magic=="DrLy" && readSdrLayer(zone, page->m_layer)) continue; |
1025 | 90.0k | if (magic=="DrLS" && readSdrLayerSet(zone, page->m_layerSet)) continue; |
1026 | 89.8k | if (magic=="DrMP" && readSdrMPageDesc(zone, *page)) continue; |
1027 | 89.8k | if (magic=="DrML" && readSdrMPageDescList(zone, *page)) continue; |
1028 | 47.1k | break; |
1029 | 89.8k | } |
1030 | 47.3k | if (ok && version==0) { |
1031 | 659 | pos=input->tell(); |
1032 | 659 | f.str(""); |
1033 | 659 | f << "SdrPageDef[masterPage]:"; |
1034 | 659 | *input >> n; |
1035 | 659 | if (pos+2+2*n>lastPos) { |
1036 | 170 | STOFF_DEBUG_MSG(("StarObjectModel::readSdrPage: n is bad\n")); |
1037 | 170 | f << "###n=" << n << ","; |
1038 | 170 | ok=false; |
1039 | 170 | } |
1040 | 489 | else { |
1041 | 489 | f << "pages=["; |
1042 | 489 | StarObjectModelInternal::Page::Descriptor desc; |
1043 | 26.8k | for (int i=0; i<int(n); ++i) { |
1044 | 26.3k | auto id=int(input->readULong(2)); |
1045 | 26.3k | f << id << ","; |
1046 | 26.3k | desc.m_masterId=id; |
1047 | 26.3k | page->m_masterPageDescList.push_back(desc); |
1048 | 26.3k | } |
1049 | 489 | f << "],"; |
1050 | 489 | } |
1051 | 659 | ascFile.addPos(pos); |
1052 | 659 | ascFile.addNote(f.str().c_str()); |
1053 | 659 | } |
1054 | | // SdrObjList::Load |
1055 | 157k | while (ok) { |
1056 | 157k | pos=input->tell(); |
1057 | 157k | if (pos+4>lastPos) |
1058 | 372 | break; |
1059 | 157k | std::shared_ptr<StarObjectSmallGraphic> smallGraphic(new StarObjectSmallGraphic(*this, true)); |
1060 | 157k | if (smallGraphic->readSdrObject(zone)) { |
1061 | 110k | page->m_objectList.push_back(smallGraphic); |
1062 | 110k | continue; |
1063 | 110k | } |
1064 | | |
1065 | 46.7k | magic=""; |
1066 | 233k | for (int i=0; i<4; ++i) magic+=char(input->readULong(1)); |
1067 | 46.7k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
1068 | 46.7k | if (magic=="DrXX" && zone.openSDRHeader(magic)) { |
1069 | 26.1k | f.str(""); |
1070 | 26.1k | f << "SdrPageDef[DrXX-" << zone.getRecordLevel() << "]:"; |
1071 | 26.1k | ascFile.addPos(pos); |
1072 | 26.1k | ascFile.addNote(f.str().c_str()); |
1073 | 26.1k | zone.closeSDRHeader("SdrPageDef"); |
1074 | 26.1k | break; |
1075 | 26.1k | } |
1076 | 20.6k | ok=false; |
1077 | 20.6k | STOFF_DEBUG_MSG(("StarObjectModel::readSdrPage: can not find end object zone\n")); |
1078 | 20.6k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
1079 | 20.6k | break; |
1080 | 46.7k | } |
1081 | 47.3k | if (ok && version>=16) { |
1082 | 10.2k | pos=input->tell(); |
1083 | 10.2k | f.str(""); |
1084 | 10.2k | f << "SdrPageDef[background-" << zone.getRecordLevel() << "]:"; |
1085 | 10.2k | bool background; |
1086 | 10.2k | *input>>background; |
1087 | 10.2k | ascFile.addPos(pos); |
1088 | 10.2k | ascFile.addNote(f.str().c_str()); |
1089 | | |
1090 | 10.2k | if (background) { |
1091 | 356 | std::shared_ptr<StarObjectSmallGraphic> smallGraphic(new StarObjectSmallGraphic(*this, true)); |
1092 | 356 | if (smallGraphic->readSdrObject(zone)) |
1093 | 40 | page->m_background=smallGraphic; |
1094 | 316 | else { |
1095 | 316 | STOFF_DEBUG_MSG(("StarObjectModel::readSdrPage: can not find backgroound object\n")); |
1096 | 316 | input->seek(pos, librevenge::RVNG_SEEK_SET); |
1097 | 316 | ok=false; |
1098 | 316 | } |
1099 | 356 | } |
1100 | 10.2k | } |
1101 | 47.3k | zone.closeRecord("SdrPageDefA1"); |
1102 | 47.3k | if (ok && hasExtraData) { |
1103 | 25.3k | lastPos=zone.getRecordLastPosition(); |
1104 | 25.3k | n=0; |
1105 | 75.7k | while (input->tell()<lastPos) { |
1106 | 52.6k | pos=input->tell(); |
1107 | 52.6k | if (!zone.openRecord()) break; |
1108 | 50.4k | f.str(""); |
1109 | 50.4k | f << "SdrPageDefB" << ++n << "[" << zone.getRecordLevel() << "]:"; |
1110 | 50.4k | if (n==1) { |
1111 | 24.7k | std::vector<uint32_t> string; |
1112 | 24.7k | if (!zone.readString(string)) { |
1113 | 159 | STOFF_DEBUG_MSG(("StarObjectModel::readSdrPage: can not find read a string\n")); |
1114 | 159 | f << "###string"; |
1115 | 159 | } |
1116 | 24.5k | else { |
1117 | | // Controls or sheet name |
1118 | 24.5k | page->m_name=libstoff::getString(string); |
1119 | 24.5k | f << page->m_name.cstr() << ","; |
1120 | 24.5k | } |
1121 | | // then if vers>={13|14|15|16} 671905111105671901000800000000000000 |
1122 | 24.7k | } |
1123 | 25.7k | else if (n==2 && pos+12==zone.getRecordLastPosition()) { |
1124 | 9.04k | f << "empty,"; |
1125 | 45.2k | for (int i=0; i<4; ++i) { // always 0 |
1126 | 36.1k | auto val=int(input->readLong(2)); |
1127 | 36.1k | if (val) f << "f" << i << "=" << val << ","; |
1128 | 36.1k | } |
1129 | 9.04k | } |
1130 | 16.6k | else if (!readSdrPageUnknownZone1(zone, zone.getRecordLastPosition())) { |
1131 | 5.73k | input->seek(pos+4, librevenge::RVNG_SEEK_SET); |
1132 | 5.73k | STOFF_DEBUG_MSG(("StarObjectModel::readSdrPage: find some unknown zone\n")); |
1133 | 5.73k | f << "###unknown"; |
1134 | 5.73k | } |
1135 | | // for version>=16?, find another zone: either an empty zone or a zone which contains many string... |
1136 | 50.4k | if (zone.getRecordLastPosition()!=input->tell()) { |
1137 | 15.6k | ascFile.addDelimiter(input->tell(),'|'); |
1138 | 15.6k | f << "#"; |
1139 | 15.6k | input->seek(zone.getRecordLastPosition(), librevenge::RVNG_SEEK_SET); |
1140 | 15.6k | } |
1141 | 50.4k | ascFile.addPos(pos); |
1142 | 50.4k | ascFile.addNote(f.str().c_str()); |
1143 | 50.4k | zone.closeRecord("SdrPageDefB"); |
1144 | 50.4k | } |
1145 | 25.3k | } |
1146 | | |
1147 | 47.3k | zone.closeSDRHeader("SdrPageDef"); |
1148 | 47.3k | return page; |
1149 | 50.1k | } |
1150 | | |
1151 | | bool StarObjectModel::readSdrPageUnknownZone1(StarZone &zone, long lastPos) |
1152 | 16.6k | { |
1153 | | // checkme: probably as draw pres: schheader: first a schheader, then 3 bool |
1154 | 16.6k | STOFFInputStreamPtr input=zone.input(); |
1155 | 16.6k | long pos=input->tell()-4; |
1156 | 16.6k | libstoff::DebugFile &ascFile=zone.ascii(); |
1157 | 16.6k | libstoff::DebugStream f; |
1158 | 16.6k | f << "Entries(SdrPageUnkn1)[" << zone.getRecordLevel() << "]:"; |
1159 | 16.6k | if (pos+28>lastPos) return false; |
1160 | 15.8k | auto val=int(input->readULong(2)); |
1161 | 15.8k | if (val!=3 && val!=7) |
1162 | 4.19k | return false; |
1163 | 11.6k | f << "f0=" << val << ","; |
1164 | 46.5k | for (int i=0; i<3; ++i) { // 1 |
1165 | 34.9k | val=int(input->readULong(1)); |
1166 | 34.9k | if (val!=1) f << "f" << i+1 << "=" << val << ","; |
1167 | 34.9k | } |
1168 | 69.8k | for (int i=0; i<5; ++i) { // g0=14|15|19, g1=1, g3=0-10, |
1169 | 58.2k | val=int(input->readLong(2)); |
1170 | 58.2k | if (val) f << "g" << i << "=" << val << ","; |
1171 | 58.2k | } |
1172 | 11.6k | val=int(input->readLong(1)); // always 1 |
1173 | 11.6k | if (val!=1) f << "f6=" << val << ","; |
1174 | 46.5k | for (int i=0; i<3; ++i) { // g5=1|1e |
1175 | 34.9k | val=int(input->readLong(2)); |
1176 | 34.9k | if (val) f << "g" << i+5 << "=" << val << ","; |
1177 | 34.9k | } |
1178 | 11.6k | std::vector<uint32_t> string; |
1179 | 11.6k | if (!zone.readString(string) || input->tell()>lastPos) |
1180 | 317 | return false; |
1181 | 11.3k | f << libstoff::getString(string).cstr() << ","; |
1182 | 11.3k | auto n=int(input->readULong(4)); |
1183 | 11.3k | if (n<0 || (lastPos-input->tell())/8<n || input->tell()+8*n>lastPos) |
1184 | 389 | return false; |
1185 | 10.9k | f << "unk=["; |
1186 | 29.4k | for (int i=0; i<n; ++i) { |
1187 | 18.5k | f << "["; |
1188 | 92.5k | for (int j=0; j<4; ++j) { |
1189 | 74.0k | val=int(input->readLong(2)); |
1190 | 74.0k | if (val) |
1191 | 29.4k | f << val << ","; |
1192 | 44.5k | else |
1193 | 44.5k | f << "_,"; |
1194 | 74.0k | } |
1195 | 18.5k | f << "],"; |
1196 | 18.5k | } |
1197 | 10.9k | f << "],"; |
1198 | 10.9k | int n1=int(lastPos-input->tell())/2; |
1199 | 10.9k | f << "unkn1=["; |
1200 | 107k | for (int i=0; i<n1; ++i) { |
1201 | 96.6k | val=int(input->readLong(2)); |
1202 | 96.6k | if (val) |
1203 | 32.5k | f << val << ","; |
1204 | 64.1k | else |
1205 | 64.1k | f << "_,"; |
1206 | 96.6k | } |
1207 | 10.9k | f << "],"; |
1208 | 10.9k | if (lastPos>input->tell()) { |
1209 | 104 | ascFile.addDelimiter(input->tell(),'|'); |
1210 | 104 | input->seek(lastPos, librevenge::RVNG_SEEK_SET); |
1211 | 104 | } |
1212 | 10.9k | ascFile.addPos(pos); |
1213 | 10.9k | ascFile.addNote(f.str().c_str()); |
1214 | 10.9k | return true; |
1215 | 11.3k | } |
1216 | | |
1217 | | bool StarObjectModel::readSdrMPageDesc(StarZone &zone, StarObjectModelInternal::Page &page) |
1218 | 16.4k | { |
1219 | 16.4k | STOFFInputStreamPtr input=zone.input(); |
1220 | | // first check magic |
1221 | 16.4k | std::string magic(""); |
1222 | 16.4k | long pos=input->tell(); |
1223 | 82.0k | for (int i=0; i<4; ++i) magic+=char(input->readULong(1)); |
1224 | 16.4k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
1225 | 16.4k | if (magic!="DrMD") return false; |
1226 | | |
1227 | 15.7k | libstoff::DebugFile &ascFile=zone.ascii(); |
1228 | 15.7k | libstoff::DebugStream f; |
1229 | 15.7k | f << "Entries(SdrMPageDesc)[" << zone.getRecordLevel() << "]:"; |
1230 | | // svdpage.cxx operator>>(..., SdrMaterPageDescriptor &) |
1231 | 15.7k | if (!zone.openSDRHeader(magic)) { |
1232 | 1.52k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
1233 | 1.52k | STOFF_DEBUG_MSG(("StarObjectModel::readSdrMPageDesc: can not open the SDR Header\n")); |
1234 | 1.52k | f << "###"; |
1235 | 1.52k | ascFile.addPos(pos); |
1236 | 1.52k | ascFile.addNote(f.str().c_str()); |
1237 | 1.52k | return false; |
1238 | 1.52k | } |
1239 | 14.2k | int version=zone.getHeaderVersion(); |
1240 | 14.2k | f << magic << ",nVers=" << version << ","; |
1241 | 14.2k | StarObjectModelInternal::Page::Descriptor desc; |
1242 | 14.2k | desc.m_masterId=int(input->readULong(2)); |
1243 | 14.2k | std::vector<int> layers; |
1244 | 470k | for (int i=0; i<32; ++i) |
1245 | 456k | layers.push_back(int(input->readULong(1))); |
1246 | 14.2k | StarObjectModelInternal::convertUint8ListToBoolList(layers, desc.m_layerList); |
1247 | 14.2k | f << desc; |
1248 | 14.2k | page.m_masterPageDescList.push_back(desc); |
1249 | 14.2k | ascFile.addPos(pos); |
1250 | 14.2k | ascFile.addNote(f.str().c_str()); |
1251 | 14.2k | zone.closeSDRHeader("SdrMPageDesc"); |
1252 | 14.2k | return true; |
1253 | 15.7k | } |
1254 | | |
1255 | | bool StarObjectModel::readSdrMPageDescList(StarZone &zone, StarObjectModelInternal::Page &page) |
1256 | 43.7k | { |
1257 | 43.7k | STOFFInputStreamPtr input=zone.input(); |
1258 | | // first check magic |
1259 | 43.7k | std::string magic(""); |
1260 | 43.7k | long pos=input->tell(); |
1261 | 218k | for (int i=0; i<4; ++i) magic+=char(input->readULong(1)); |
1262 | 43.7k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
1263 | 43.7k | if (magic!="DrML") return false; |
1264 | | |
1265 | 43.7k | libstoff::DebugFile &ascFile=zone.ascii(); |
1266 | 43.7k | libstoff::DebugStream f; |
1267 | 43.7k | f << "Entries(SdrMPageList)[" << zone.getRecordLevel() << "]:"; |
1268 | | // svdpage.cxx operator>>(..., SdrMaterPageDescListriptor &) |
1269 | 43.7k | if (!zone.openSDRHeader(magic)) { |
1270 | 1.00k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
1271 | 1.00k | STOFF_DEBUG_MSG(("StarObjectModel::readSdrMPageDescList: can not open the SDR Header\n")); |
1272 | 1.00k | f << "###"; |
1273 | 1.00k | ascFile.addPos(pos); |
1274 | 1.00k | ascFile.addNote(f.str().c_str()); |
1275 | 1.00k | return false; |
1276 | 1.00k | } |
1277 | 42.7k | int version=zone.getHeaderVersion(); |
1278 | 42.7k | uint16_t n; |
1279 | 42.7k | *input>>n; |
1280 | 42.7k | f << magic << ",nVers=" << version << ",N=" << n << ","; |
1281 | 42.7k | ascFile.addPos(pos); |
1282 | 42.7k | ascFile.addNote(f.str().c_str()); |
1283 | | |
1284 | 42.7k | long lastPos=zone.getRecordLastPosition(); |
1285 | 57.0k | for (int i=0; i<n; ++i) { |
1286 | 17.2k | pos=input->tell(); |
1287 | 17.2k | if (pos<lastPos && readSdrMPageDesc(zone, page)) continue; |
1288 | 3.00k | STOFF_DEBUG_MSG(("StarObjectModel::readSdrMPageDescList: can not find a page desc\n")); |
1289 | 3.00k | ascFile.addPos(pos); |
1290 | 3.00k | ascFile.addNote("SdrMPageList:###pageDesc"); |
1291 | 3.00k | input->seek(pos, librevenge::RVNG_SEEK_SET); |
1292 | 3.00k | break; |
1293 | 17.2k | } |
1294 | | |
1295 | 42.7k | zone.closeSDRHeader("SdrMPageList"); |
1296 | 42.7k | return true; |
1297 | 43.7k | } |
1298 | | |
1299 | | // vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: |