1 /* This file is part of the KDE project
2    Copyright (C) 2001 Enno Bartels <ebartels@nwn.de>
3 
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Library General Public
6    License as published by the Free Software Foundation; either
7    version 2 of the License, or (at your option) any later version.
8 
9    This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Library General Public License for more details.
13 
14    You should have received a copy of the GNU Library General Public License
15    along with this library; see the file COPYING.LIB.  If not, write to
16    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18 */
19 
20 #include "applixspreadimport.h"
21 
22 #include <QMessageBox>
23 #include <QStringList>
24 #include <QRegExp>
25 #include <QList>
26 #include <QTextStream>
27 #include <QByteArray>
28 #include <kdebug.h>
29 #include <math.h>
30 #include <KoFilterChain.h>
31 #include <kpluginfactory.h>
32 #include <sheets/Util.h>
33 
34 K_PLUGIN_FACTORY_WITH_JSON(APPLIXSPREADImportFactory, "calligra_filter_applixspread2kspread.json",
35                            registerPlugin<APPLIXSPREADImport>();)
36 
37 
APPLIXSPREADImport(QObject * parent,const QVariantList &)38 APPLIXSPREADImport::APPLIXSPREADImport(QObject *parent, const QVariantList&)
39         : KoFilter(parent)
40 {
41 }
42 
nextLine(QTextStream & stream)43 QString APPLIXSPREADImport::nextLine(QTextStream & stream)
44 {
45     if (!m_nextPendingLine.isNull()) {
46         const QString s = m_nextPendingLine;
47         m_nextPendingLine.clear();
48         return s;
49     }
50 
51     QString s = stream.readLine();
52     m_instep += s.length();
53     if (m_instep > m_stepsize) {
54         m_instep = 0;
55         m_progress += 2;
56         emit sigProgress(m_progress);
57     }
58     return s;
59 }
60 
61 struct t_mycolor {
62     int r;
63     int g;
64     int b;
65 
66     int c;
67     int m;
68     int y;
69     int k;
70 };
71 
72 struct t_rc {
73     QStringList tabname;
74     QStringList rc;
75 };
76 
77 // Store shared formula definitions
78 struct t_sharedFormula {
79     int origRow;
80     int origColumn;
81     QString formula;
82 };
83 
convert(const QByteArray & from,const QByteArray & to)84 KoFilter::ConversionStatus APPLIXSPREADImport::convert(const QByteArray& from, const QByteArray& to)
85 {
86 
87     if (to != "application/x-kspread" || from != "application/x-applix-spreadsheet")
88         return KoFilter::NotImplemented;
89 
90     QFile in(m_chain->inputFile());
91     if (!in.open(QIODevice::ReadOnly)) {
92         kError(30502) << "Unable to open input file!" << endl;
93         in.close();
94         return KoFilter::FileNotFound;
95     }
96 
97     QString str;
98     QList<t_mycolor*> mcol;
99 
100     str += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
101            "<!DOCTYPE spreadsheet>\n"
102            "<spreadsheet mime=\"application/x-kspread\" editor=\"KSpread\" >\n"
103            " <paper format=\"A4\" orientation=\"Portrait\" >\n"
104            "  <borders right=\"20\" left=\"20\" bottom=\"20\" top=\"20\" />\n"
105            "  <head/>\n"
106            "  <foot/>\n"
107            " </paper>\n"
108 //    str += " <locale positivePrefixCurrencySymbol=\"True\" negativeMonetarySignPosition=\"1\" negativePrefixCurrencySymbol=\"True\" fracDigits=\"2\" thousandsSeparator=\" \" dateFormat=\"%A, %e. %B %Y\" timeFormat=\"%H:%M:%S\" monetaryDecimalSymbol=\",\" weekStartsMonday=\"True\" currencySymbol=\"DM\" negativeSign=\"-\" positiveSign=\"\" positiveMonetarySignPosition=\"1\" decimalSymbol=\",\" monetaryThousandsSeparator=\" \" dateFormatShort=\"%d.%m.%Y\" />\n";
109            " <map markerColumn=\"1\" activeTable=\"Table1\" markerRow=\"1\" >\n";
110 //      str += "  <table columnnumber=\"0\" borders=\"0\" hide=\"0\" hidezero=\"0\" firstletterupper=\"0\" grid=\"1\" formular=\"0\" lcmode=\"0\" name=\"Tabelle1\" >\n";
111 
112 
113     // QTextStream
114     QTextStream stream(&in);
115     m_stepsize = in.size() / 50;
116     m_instep   = 0;
117     m_progress = 0;
118     int  pos;
119     QString  tabctr ;  // Tab control (current tab name)
120     QStringList typefacetab;
121     QHash<QString, t_sharedFormula> sharedFormulas;
122 
123     t_rc my_rc;
124 
125 
126 
127     /**************************************************************************
128      * Read header                                                            *
129      **************************************************************************/
130     if (! readHeader(stream)) return KoFilter::StupidError;
131 
132     while (!stream.atEnd()) {
133         // Read one line
134         QString mystr = nextLine(stream);
135 
136         kDebug() << "INPUT :" << mystr;
137 
138 
139         /**********************************************************************
140          *  Looking for the colormap                                          *
141          **********************************************************************/
142         if (mystr.startsWith("COLORMAP")) {
143             readColormap(stream, mcol);
144         }
145 
146         /**********************************************************************
147          *  Looking for the typeface table                                    *
148          **********************************************************************/
149         else if (mystr.startsWith("TYPEFACE TABLE")) {
150             readTypefaceTable(stream, typefacetab);
151         }
152 
153         /**********************************************************************
154          *  Looking for some View-Information                                 *
155          **********************************************************************/
156         else if (mystr.startsWith("View Start, Name:")) {
157             readView(stream, mystr, my_rc);
158         }
159 
160 
161         /**********************************************************************
162          *   Detect ( at the first place of the Line                          *
163          **********************************************************************/
164         else if (mystr[0] == '(') {
165 
166             // Delete  '('
167             mystr.remove(0, 1);
168 
169             // Remember length of the string
170             if (mystr.length() >= 80 - 1) {
171                 //kDebug() << " Line >= 80 chars";
172                 bool ok = true;
173                 do {
174                     QString mystrn = nextLine(stream);
175                     if (mystrn[0] == ' ') {
176                         mystrn.remove(0, 1);
177                         mystr += mystrn;
178                     } else {
179                         m_nextPendingLine = mystrn;
180                         ok = false;
181                     }
182                 } while (ok);
183                 kDebug() << " Long line -> new input line:" << mystr;
184             }
185 
186             // Search for ')'
187             pos = mystr.indexOf(')');
188             QString typestr = mystr.left(pos);
189 
190             // Delete typeformat info incl. Space
191             mystr.remove(0, pos + 1);
192 
193             // At this point mystr looks like " A!E15: 10"
194             Q_ASSERT(mystr.startsWith(' '));
195 
196             // Extract table number/name
197             pos = mystr.indexOf('!');
198 
199             // Copy tabnumber information
200             QString tabnostr = mystr.left(pos).mid(1);
201 
202             // Delete tabnumber information
203             mystr.remove(0, pos + 1);
204 
205             // At this point mystr looks like "E15: 10"
206             pos = 0;
207             while (mystr[pos].isLetter()) {
208                 ++pos;
209             }
210             const QString cellcolstr = mystr.mid(0, pos);
211             // Transform ascii column to int column
212             const int icol = translateColumnNumber(cellcolstr);
213 
214             int endPos = pos;
215             while (mystr[endPos].isDigit()) {
216                 ++endPos;
217             }
218 
219             const QString rowstr = mystr.mid(pos, endPos - pos);
220             bool ok;
221             const int irow = rowstr.toInt(&ok);
222             Q_ASSERT(ok);
223 
224             // OK, what do we have now?
225             const QChar contentType = mystr.at(endPos);
226 
227             // Delete cellnumber information
228             mystr.remove(0, endPos + 1);
229             if (mystr.startsWith(' ')) {
230                 mystr.remove(0, 1);
231             }
232 
233             // ';' // first instance (definition) of a shared formula
234             // '.' // instance (usage) of a shared formula
235             // ':' // simple value
236 
237             if (contentType == ';' || contentType == '.') {
238                 // Skip the value
239                 int pos = 0;
240                 while (!mystr.at(pos).isSpace()) {
241                     ++pos;
242                 }
243                 while (mystr.at(pos).isSpace()) {
244                     ++pos;
245                 }
246                 kDebug() << "Skipping value" << mystr.mid(0, pos);
247                 mystr.remove(0, pos);
248 
249                 if (contentType == ';') {
250                     if (mystr.at(0) == '+')
251                         mystr[0] = '=';
252                     Q_ASSERT(mystr.at(0) == '=');
253                 }
254             }
255 
256             // Replace part for this characters: <, >, &
257             mystr.replace(QRegExp("&"), "&amp;");
258             mystr.replace(QRegExp("<"), "&lt;");
259             mystr.replace(QRegExp(">"), "&gt;");
260 
261 
262             // Replace part for Applix Characters
263             bool foundSpecialCharakter;
264 
265             do {
266                 // initialize
267                 foundSpecialCharakter = false;
268 
269                 pos = mystr.indexOf('^');
270 
271                 // is there a special character ?
272                 if (pos > -1) {
273                     // i have found a special character !
274                     foundSpecialCharakter = true;
275 
276                     // translate the applix special character
277                     const QChar newchar = specCharfind(mystr[pos+1], mystr[pos+2]);
278 
279                     // replace the character
280                     mystr.replace(pos, 3, newchar);
281                 }
282 
283             } while (foundSpecialCharakter == true);
284 
285 
286             // examine the typestring
287             // split typestring in 3 parts by an |
288 
289             const int pos1 = typestr.indexOf('|');
290             const int pos2 = typestr.lastIndexOf('|');
291             const QString typeFormStr = typestr.left(pos1);
292             const QString typeCharStr = typestr.mid(pos1 + 1,  pos2 - pos1 - 1);
293             const QString typeCellStr = typestr.right(typestr.length() - pos2 - 1);
294 
295             // Is it a new table
296             if (tabctr != tabnostr) {
297                 // is it not the first table
298                 if (!(tabctr.isNull()))  str += "  </table>\n";
299 
300                 str += "  <table columnnumber=\"0\" borders=\"0\" hide=\"0\" hidezero=\"0\" firstletterupper=\"0\" grid=\"1\" formular=\"0\" lcmode=\"0\" name=\"" +
301                        tabnostr +
302                        "\" >\n";
303 
304                 tabctr = tabnostr;
305 
306                 // Searching for the rowcol part and adding to the hole string
307                 pos = my_rc.tabname.indexOf(tabnostr);
308                 if (pos > -1) str += my_rc.rc[pos];
309             }
310 
311             //kDebug()<<" Data : Text :"<<mystr<<" tab :"<<tabnostr<<""<< cellnostr <<"" <<ccol<<"" << irow<<""<< typeFormStr<<"" <<typeCharStr<<"" <<typeCellStr;
312 
313             /********************************************************************
314              * Support for shared formulas                                      *
315              ********************************************************************/
316 
317             if (contentType == ';') {
318 
319                 mystr = convertFormula(mystr);
320 
321                 const QString formulaRefLine = nextLine(stream); // "Formula: 358"
322                 kDebug() << "shared formula: next line is" << formulaRefLine;
323                 if (!formulaRefLine.startsWith("Formula: ")) {
324                     kError() << "Missing formula ID after" << mystr;
325                 } else {
326                     const QString key = formulaRefLine.mid(9);
327                     t_sharedFormula sf;
328                     sf.origColumn = icol;
329                     sf.origRow = irow;
330                     sf.formula = mystr;
331                     sharedFormulas.insert(key, sf);
332                 }
333             } else if (contentType == '.') {
334                 const QString key = mystr;
335                 const t_sharedFormula sf = sharedFormulas.value(key);
336 
337                 // adjust the formula: if it came from C1, with =A1+B1,
338                 // and we're now in C3, then it needs to become =A3+B3, just like copy/paste would do.
339                 mystr = Calligra::Sheets::Util::adjustFormulaReference(sf.formula, sf.origRow, sf.origColumn, irow, icol);
340             }
341 
342             /********************************************************************
343              * examine character format String, split it up in basic parts      *
344              ********************************************************************/
345             int bold = 0, italic = 0, underline = 0, fontsize = 12, fontnr = -1;
346             int fg = -1; // fg = foregound
347 
348             const QStringList typeCharList = typeCharStr.split(',', QString::SkipEmptyParts);
349             Q_FOREACH(const QString& typeChar, typeCharList) {
350                 // Output
351                 kDebug() << "typeChar: " << typeChar;
352 
353                 if (typeChar == "B") {
354                     kDebug() << " bold";
355                     bold  = 1;
356                 } else if (typeChar == "I") {
357                     kDebug() << "   = italic";
358                     italic = 1;
359                 } else if (typeChar == "U") {
360                     kDebug() << "   = underline";
361                     underline = 1;
362                 } else if (typeChar.startsWith("FG")) {
363                     fg = typeChar.mid(2).toInt();
364                     kDebug() << "  = Colornr" << fg;
365                 } else if (typeChar.startsWith("TF")) {
366                     fontnr = typeChar.mid(2).toInt();
367                     kDebug() << " = Font :" << fontnr << "" << typefacetab[fontnr];
368                 } else if (typeChar.startsWith('P')) {
369                     fontsize = typeChar.mid(1).toInt();
370                     kDebug() << "   = Fontsize" << fontsize;
371                 } else {
372                     kDebug() << "   = ??? Unknown typeChar:" << typeChar;
373                 }
374             }
375             kDebug();
376 
377 
378             /********************************************************************
379              * examine pos format String, split it up in basic parts           *
380              ********************************************************************/
381             int align = 0, valign = 0;
382 
383             const QStringList typeFormList = typeFormStr.split(',', QString::SkipEmptyParts);
384             Q_FOREACH(const QString& typeFormat, typeFormList) {
385                 // Grep horizontal alignment
386                 if (typeFormat == "1") {
387                     kDebug() << " = left align";
388                     align = 1; // left
389                 } else if (typeFormat == "2") {
390                     kDebug() << " = right align";
391                     align = 3; // right
392                 } else if (typeFormat == "3") {
393                     kDebug() << " = center align";
394                     align = 2; // center
395                 }
396 
397                 // Grep vertical alignment
398                 else if (typeFormat == "VT") {
399                     kDebug() << " = top valign";
400                     valign =  1; // top
401                 } else if (typeFormat == "VC") {
402                     kDebug() << " = center valign";
403                     valign =  0; // center - default (2)
404                 } else if (typeFormat == "VB") {
405                     kDebug() << " = bottom valign";
406                     valign =  3; // bottom
407                 } else {
408                     kDebug() << "   = ??? unknown typeFormat" << typeFormat;
409                 }
410             }
411 
412 
413             /********************************************************************
414              * examine cell format String, split it up in basic parts           *
415              ********************************************************************/
416             int topPenWidth = 0, bottomPenWidth = 0, leftPenWidth = 0, rightPenWidth = 0, fg_bg = -1;
417             int topPenStyle = 0, bottomPenStyle = 0, leftPenStyle = 0, rightPenStyle = 0;
418             int brushstyle = 0,     brushcolor = 1;
419             int topbrushstyle = 0,  topbrushcolor = 1, topfg_bg = 1;
420             int leftbrushstyle = 0, leftbrushcolor = 1, leftfg_bg = 1;
421             int rightbrushstyle = 0, rightbrushcolor = 1, rightfg_bg = 1;
422             int bottombrushstyle = 0, bottombrushcolor = 1, bottomfg_bg = 1;
423 
424             const QStringList typeCellList = typeCellStr.split(',', QString::SkipEmptyParts);
425             Q_FOREACH(/*can't use const QString&*/ QString typeCell, typeCellList) {
426 
427                 if (typeCell[0] == 'T') {
428                     kDebug() << " = top";
429                     transPenFormat(typeCell, &topPenWidth, &topPenStyle);
430 
431                     if (typeCell.length() > 2) {
432                         typeCell.remove(0, 2);
433                         filterSHFGBG(typeCell, &topbrushstyle, &topbrushcolor, &topfg_bg);
434                     }
435 
436                 }
437 
438                 else if (typeCell[0] == 'B') {
439                     kDebug() << " = bottom";
440                     transPenFormat(typeCell, &bottomPenWidth, &bottomPenStyle);
441 
442                     if (typeCell.length() > 2) {
443                         typeCell.remove(0, 2);
444                         filterSHFGBG(typeCell, &bottombrushstyle, &bottombrushcolor, &bottomfg_bg);
445                     }
446                 }
447 
448                 else if (typeCell[0] == 'L') {
449                     kDebug() << " = left";
450                     transPenFormat(typeCell, &leftPenWidth, &leftPenStyle);
451 
452                     if (typeCell.length() > 2) {
453                         typeCell.remove(0, 2);
454                         filterSHFGBG(typeCell, &leftbrushstyle, &leftbrushcolor, &leftfg_bg);
455                     }
456                 }
457 
458                 else if (typeCell[0] == 'R') {
459                     kDebug() << " = right";
460                     transPenFormat(typeCell, &rightPenWidth, &rightPenStyle);
461 
462                     if (typeCell.length() > 2) {
463                         typeCell.remove(0, 2);
464                         filterSHFGBG(typeCell, &rightbrushstyle, &rightbrushcolor, &rightfg_bg);
465                     }
466                 }
467 
468                 else if ((typeCell.startsWith("SH")) || (typeCell.startsWith("FG")) ||
469                          (typeCell.startsWith("BG"))) {
470                     kDebug() << " =";
471                     filterSHFGBG(typeCell, &brushstyle, &fg_bg, &brushcolor);
472                 }
473 
474                 else {
475                     kDebug() << "   = ??? unknown typeCell" << typeCell;
476                 }
477 
478             }
479 
480 
481 
482 
483 
484             QString col;
485 
486             // create kspread fileformat output
487             str += "   <cell row=\"" + QString::number(irow) + "\""
488                    " column=\""      + QString::number(icol) + "\">\n";
489             if (bold == 1  || italic == 1 || underline == 1 ||
490                     align != 0 || valign != 0 ||
491                     topPenStyle  != 0  || bottomPenStyle != 0 ||
492                     leftPenStyle != 0  || rightPenStyle  != 0 || fg != -1 || fg_bg != -1 ||
493                     fontsize != 12 || brushstyle != 0 || fontnr != -1) {
494                 str += "    <format";
495                 if (brushstyle != 0) {
496                     str += " brushstyle=\""  + QString::number(brushstyle) + "\" "
497                            " brushcolor=\"" +
498                            writeColor(mcol.at(brushcolor)) +
499                            "\"";
500                 }
501 
502                 if (align   != 0)  str += " align=\""  + QString::number(align) + "\" ";
503                 if (valign  != 0)  str += " alignY=\"" + QString::number(valign) + "\" ";
504                 if (fg_bg != -1) {
505                     str += " bgcolor=\"" +
506                            writeColor(mcol.at(fg_bg)) +
507                            "\" ";
508                 }
509                 str += ">\n";
510 
511                 // Font color
512                 if (fg != -1) {
513                     str += "    <pen width=\"0\" style=\"1\" color=\"" +
514                            writeColor(mcol.at(fg)) +
515                            "\" />\n";
516                 }
517 
518                 // Left border
519                 if (leftPenWidth > 0) {
520                     str += "    <left-border>\n";
521                     col = writeColor(mcol.at(leftfg_bg));
522                     writePen(str, leftPenWidth, leftPenStyle, col);
523                     str += "    </left-border>\n";
524                 }
525 
526                 // Right border
527                 if (rightPenWidth > 0) {
528                     str += "    <right-border>\n";
529                     col = writeColor(mcol.at(rightfg_bg));
530                     writePen(str, rightPenWidth, rightPenStyle, col);
531                     str += "    </right-border>\n";
532                 }
533 
534                 // Bottom border
535                 if (bottomPenWidth > 0) {
536                     str += "    <bottom-border>\n";
537                     col = writeColor(mcol.at(bottomfg_bg));
538                     writePen(str, bottomPenWidth, bottomPenStyle, col);
539                     str += "    </bottom-border>\n";
540                 }
541 
542                 // Top border
543                 if (topPenWidth > 0) {
544                     str += "    <top-border>\n";
545                     col = writeColor(mcol.at(topfg_bg));
546                     writePen(str, topPenWidth, topPenStyle, col);
547                     str += "    </top-border>\n";
548                 }
549 
550                 // Font (size and family)
551                 if ((fontsize != 12) || (fontnr != -1)) {
552                     str += "     <font ";
553                     // Fontsize
554                     if (fontsize != 12) {
555                         str += "size=\"" +
556                                QString::number(fontsize) +
557                                "\" ";
558                     }
559                     // Fontfamily
560                     if (fontnr != -1) {
561                         str += "family=\"" +
562                                typefacetab[fontnr].toLatin1() +
563                                "\" ";
564                     }
565                     str += "weight=\"0\"";
566 
567                     if (italic    == 1) str += " italic=\"yes\"";
568                     if (bold      == 1) str += " bold=\"yes\"";
569                     if (underline == 1) str += " underline=\"yes\"";
570 
571                     str += " />\n";
572                 }
573                 str += "    </format>\n";
574             }
575             str += "    <text>" + mystr + "</text>\n"
576                    "   </cell>\n";
577         }
578 
579     }
580     emit sigProgress(100);
581 
582     str += "  </table>\n"
583            " </map>\n"
584            "</spreadsheet>\n";
585 //  str += "</DOC>\n";
586 
587     kDebug() << "Text" << str;
588 
589     KoStoreDevice* out = m_chain->storageFile("root", KoStore::Write);
590 
591     if (!out) {
592         kError(38000/*30502*/) << "Unable to open output file!" << endl;
593         in.close();
594         return KoFilter::StorageCreationError;
595     }
596 
597     QByteArray cstring = str.toUtf8();
598     out->write(cstring, cstring.length());
599 
600     in.close();
601     return KoFilter::OK;
602 }
603 
604 
605 
606 
607 /******************************************************************************
608  *  function: specCharfind                                                    *
609  ******************************************************************************/
610 QChar
specCharfind(QChar a,QChar b)611 APPLIXSPREADImport::specCharfind(QChar a, QChar b)
612 {
613     QChar chr;
614 
615     if ((a == 'n') && (b == 'p'))  chr = 0x00DF; // 'ß';
616 
617 
618     else if ((a == 'n') && (b == 'c'))  chr = 0x00D2; // 'Ò';
619     else if ((a == 'p') && (b == 'c'))  chr = 0x00F2; // 'ò';
620 
621     else if ((a == 'n') && (b == 'd'))  chr = 0x00D3; // 'Ó';
622     else if ((a == 'p') && (b == 'd'))  chr = 0x00F3; // 'ó';
623 
624     else if ((a == 'n') && (b == 'e'))  chr = 0x00D4; // 'Ô';
625     else if ((a == 'p') && (b == 'e'))  chr = 0x00F4; // 'ô';
626 
627     else if ((a == 'n') && (b == 'f'))  chr = 0x00D5; // 'Õ';
628     else if ((a == 'p') && (b == 'f'))  chr = 0x00F5; // 'õ';
629 
630     else if ((a == 'n') && (b == 'g'))  chr = 0x00D6; // 'Ö';
631     else if ((a == 'p') && (b == 'g'))  chr = 0x00F6; // 'ö';
632 
633 
634 
635     else if ((a == 'n') && (b == 'j'))  chr = 0x00D9; // 'Ù';
636     else if ((a == 'p') && (b == 'j'))  chr = 0x00F9; // 'ù';
637 
638     else if ((a == 'n') && (b == 'k'))  chr = 0x00DA; // 'Ú';
639     else if ((a == 'p') && (b == 'k'))  chr = 0x00FA; // 'ú';
640 
641     else if ((a == 'n') && (b == 'l'))  chr = 0x00DB; // 'Û';
642     else if ((a == 'p') && (b == 'l'))  chr = 0x00FB; // 'û';
643 
644     else if ((a == 'n') && (b == 'm'))  chr = 0x00DC; // 'Ü';
645     else if ((a == 'p') && (b == 'm'))  chr = 0x00FC; // 'ü';
646 
647 
648 
649     else if ((a == 'm') && (b == 'a'))  chr = 0x00C0; // 'À';
650     else if ((a == 'o') && (b == 'a'))  chr = 0x00E0; // 'à';
651 
652     else if ((a == 'm') && (b == 'b'))  chr = 0x00C1; // 'Á';
653     else if ((a == 'o') && (b == 'b'))  chr = 0x00E1; // 'á';
654 
655     else if ((a == 'm') && (b == 'c'))  chr = 0x00C2; // 'Â';
656     else if ((a == 'o') && (b == 'c'))  chr = 0x00E2; // 'â';
657 
658     else if ((a == 'm') && (b == 'd'))  chr = 0x00C3; // 'Ã';
659     else if ((a == 'o') && (b == 'd'))  chr = 0x00E3; // 'ã';
660 
661     else if ((a == 'm') && (b == 'e'))  chr = 0x00C4; // 'Ä';
662     else if ((a == 'o') && (b == 'e'))  chr = 0x00E4; // 'ä';
663 
664     else if ((a == 'm') && (b == 'f'))  chr = 0x00C5; // 'Å';
665     else if ((a == 'o') && (b == 'f'))  chr = 0x00E5; // 'å';
666 
667     else if ((a == 'm') && (b == 'g'))  chr = 0x00C6; // 'Æ';
668     else if ((a == 'o') && (b == 'g'))  chr = 0x00E6; // 'æ';
669 
670 
671 
672     else if ((a == 'm') && (b == 'i'))  chr = 0x00C8; // 'È';
673     else if ((a == 'o') && (b == 'i'))  chr = 0x00E8; // 'è';
674 
675     else if ((a == 'm') && (b == 'j'))  chr = 0x00C9; // 'É';
676     else if ((a == 'o') && (b == 'j'))  chr = 0x00E9; // 'é';
677 
678     else if ((a == 'm') && (b == 'k'))  chr = 0x00CA; // 'Ê';
679     else if ((a == 'o') && (b == 'k'))  chr = 0x00EA; // 'ê';
680 
681     else if ((a == 'm') && (b == 'l'))  chr = 0x00CB; // 'Ë';
682     else if ((a == 'o') && (b == 'l'))  chr = 0x00EB; // 'ë';
683 
684 
685 
686     else if ((a == 'm') && (b == 'm'))  chr = 0x00CC; // 'Ì';
687     else if ((a == 'o') && (b == 'm'))  chr = 0x00EC; // 'ì';
688 
689     else if ((a == 'm') && (b == 'n'))  chr = 0x00CD; // 'Í';
690     else if ((a == 'o') && (b == 'n'))  chr = 0x00ED; // 'í';
691 
692     else if ((a == 'm') && (b == 'o'))  chr = 0x00CE; // 'Î';
693     else if ((a == 'o') && (b == 'o'))  chr = 0x00EE; // 'î';
694 
695     else if ((a == 'm') && (b == 'p'))  chr = 0x00CF; // 'Ï';
696     else if ((a == 'o') && (b == 'p'))  chr = 0x00EF; // 'ï';
697 
698 
699     else if ((a == 'n') && (b == 'b'))  chr = 0x00D1; // 'Ñ';
700     else if ((a == 'p') && (b == 'b'))  chr = 0x00F1; // 'ñ';
701 
702 
703     else if ((a == 'k') && (b == 'c'))  chr = 0x00A2; // '¢';
704     else if ((a == 'k') && (b == 'j'))  chr = 0x00A9; // '©';
705     else if ((a == 'l') && (b == 'f'))  chr = 0x00B5; // 'µ';
706     else if ((a == 'n') && (b == 'i'))  chr = 0x00D8; // 'Ø';
707     else if ((a == 'p') && (b == 'i'))  chr = 0x00F8; // 'ø';
708 
709     else if ((a == 'l') && (b == 'j'))  chr = 0x00B9; // '¹';
710     else if ((a == 'l') && (b == 'c'))  chr = 0x00B2; // '²';
711     else if ((a == 'l') && (b == 'd'))  chr = 0x00B3; // '³';
712 
713     else if ((a == 'l') && (b == 'm'))  chr = 0x0152; // 'Œ';
714     else if ((a == 'l') && (b == 'n'))  chr = 0x0153; // 'œ';
715     else if ((a == 'l') && (b == 'o'))  chr = 0x0178; // 'Ÿ';
716 
717     else if ((a == 'l') && (b == 'a'))  chr = 0x00B0; // '°';
718 
719     else if ((a == 'k') && (b == 'o'))  chr = 0x00AE; // '®';
720     else if ((a == 'k') && (b == 'h'))  chr = 0x00A7; // '§';
721     else if ((a == 'k') && (b == 'd'))  chr = 0x00A3; // '£';
722 
723     else if ((a == 'p') && (b == 'a'))  chr = 0x00F0; // 'ð';
724     else if ((a == 'n') && (b == 'a'))  chr = 0x00D0; // 'Ð';
725 
726     else if ((a == 'l') && (b == 'l'))  chr = 0x00BB; // '»';
727     else if ((a == 'k') && (b == 'l'))  chr = 0x00AB; // '«';
728 
729     else if ((a == 'l') && (b == 'k'))  chr = 0x00BA; // 'º';
730 
731     else if ((a == 'l') && (b == 'h'))  chr = 0x00B7; // '·';
732 
733     else if ((a == 'k') && (b == 'b'))  chr = 0x00A1; // '¡';
734 
735     else if ((a == 'k') && (b == 'e'))  chr = 0x20AC; // '€';
736 
737     else if ((a == 'l') && (b == 'b'))  chr = 0x00B1; // '±';
738 
739     else if ((a == 'l') && (b == 'p'))  chr = 0x00BF; // '¿';
740 
741     else if ((a == 'k') && (b == 'f'))  chr = 0x00A5; // '¥';
742 
743     else if ((a == 'p') && (b == 'o'))  chr = 0x00FE; // 'þ';
744     else if ((a == 'n') && (b == 'o'))  chr = 0x00DE; // 'Þ';
745 
746     else if ((a == 'n') && (b == 'n'))  chr = 0x00DD; // 'Ý';
747     else if ((a == 'p') && (b == 'n'))  chr = 0x00FD; // 'ý';
748     else if ((a == 'p') && (b == 'p'))  chr = 0x00FF; // 'ÿ';
749 
750     else if ((a == 'k') && (b == 'k'))  chr = 0x00AA; // 'ª';
751 
752     else if ((a == 'k') && (b == 'm'))  chr = 0x00AC; // '¬';
753     else if ((a == 'p') && (b == 'h'))  chr = 0x00F7; // '÷';
754 
755     else if ((a == 'k') && (b == 'g'))  chr = 0x007C; // '|';
756 
757     else if ((a == 'l') && (b == 'e'))  chr = 0x0027; // '\'';
758 
759     else if ((a == 'k') && (b == 'i'))  chr = 0x0161; // 'š';
760 
761     else if ((a == 'k') && (b == 'n'))  chr = 0x00AD; // '­';
762 
763     else if ((a == 'k') && (b == 'p'))  chr = 0x00AF; // '¯';
764 
765     else if ((a == 'l') && (b == 'g'))  chr = 0x00B6; // '¶';
766 
767     else if ((a == 'l') && (b == 'i'))  chr = 0x017E; // 'ž';
768 
769     else if ((a == 'm') && (b == 'h'))  chr = 0x00C7; // 'Ç';
770     else if ((a == 'o') && (b == 'h'))  chr = 0x00E7; // 'ç';
771 
772     else if ((a == 'n') && (b == 'h'))  chr = 0x00D7; // '×';
773 
774     else if ((a == 'k') && (b == 'a'))  chr = 0x0020; // ' ';
775 
776     else if ((a == 'a') && (b == 'j'))  chr = 0x0021; // '!';
777 
778     else  chr = 0x0023; // '#';
779 
780     return chr;
781 }
782 
783 
784 
785 /******************************************************************************
786  *  function:   writePen                                                      *
787  ******************************************************************************/
788 void
writePen(QString & str,int penwidth,int penstyle,const QString & framecolor)789 APPLIXSPREADImport::writePen(QString &str, int penwidth, int penstyle, const QString &framecolor)
790 {
791     str += "     <pen width=\"" +
792 
793     // width of the pen
794            QString::number(penwidth) +
795            "\" style=\"" +
796 
797     // style of the pen
798            QString::number(penstyle) +
799            "\" color=\"" +
800 
801     // color of the pen
802            framecolor +
803            "\" />\n";
804 
805 }
806 
807 
808 
809 /******************************************************************************
810  *  function:   writeColor                                                    *
811  ******************************************************************************/
812 QString
writeColor(t_mycolor * mc)813 APPLIXSPREADImport::writeColor(t_mycolor *mc)
814 {
815     char rgb[20];
816 
817 //    printf ("                 WriteColor: <%d>-<%d>-<%d>   <%d>-<%d>-<%d>-<%d>\n",
818 //            mc->r, mc->g, mc->b,
819 //            mc->c, mc->m, mc->y, mc->k);
820 
821     sprintf(rgb, "#%02X%02X%02X", mc->r, mc->g, mc->b);
822     QString bla = rgb;
823 
824 
825     return bla;
826 }
827 
828 
829 
830 
831 /******************************************************************************
832  *  function:   readTypefaceTable                                             *
833  ******************************************************************************/
834 void
readTypefaceTable(QTextStream & stream,QStringList & typefacetab)835 APPLIXSPREADImport::readTypefaceTable(QTextStream &stream, QStringList &typefacetab)
836 {
837     int tftabCounter = 0;
838     QString mystr;
839 
840     // Read the colormap
841     kDebug() << "Reading typeface table:";
842 
843     bool ok = true;
844     do {
845         mystr = nextLine(stream);
846         // FIXME: What happens if the magic words are not present in the stream?
847         if (mystr == "END TYPEFACE TABLE") ok = false;
848         else {
849             //printf ("  %2d: <%s>\n", tftabCounter, mystr.toLatin1());
850             typefacetab.append(mystr);
851             tftabCounter++;
852         }
853     } while (ok == true);
854 
855     kDebug() << "... done";
856 }
857 
858 
859 
860 /******************************************************************************
861  *  function:   readColormap                                                  *
862  ******************************************************************************/
863 void
readColormap(QTextStream & stream,QList<t_mycolor * > & mcol)864 APPLIXSPREADImport::readColormap(QTextStream &stream,  QList<t_mycolor*> &mcol)
865 {
866     int contcount, pos;
867 
868     QString colstr, mystr;
869     kDebug() << "Reading colormap:";
870 
871     bool ok = true;
872 
873     do {
874 
875         mystr = nextLine(stream).trimmed();
876 
877         if (mystr == "END COLORMAP") ok = false;
878         else {
879             kDebug() << "  ->" << mystr;
880 
881             // Count the number of  whitespaces
882             contcount = mystr.count(' ');
883             kDebug() << "contcount:" << contcount;
884             contcount -= 5;
885 
886             // Begin off interest
887             pos = mystr.indexOf(" 0 ");
888 
889             // get colorname
890             colstr = mystr.left(pos);
891             mystr.remove(0, pos + 1);
892             mystr = mystr.trimmed();
893 
894             t_mycolor *tmc = new t_mycolor;
895 
896             // get sub colors
897             pos = sscanf(mystr.toLatin1(), "0 %d %d %d %d 0",
898                          &tmc->c, &tmc->m, &tmc->y, &tmc->k);
899 
900             printf("  - <%-20s> <%-15s> <%3d> <%3d> <%3d> <%3d>  pos: %d\n",
901                    mystr.toLatin1().data(),
902                    colstr.toLatin1().data(),
903                    tmc->c, tmc->m, tmc->y, tmc->k, pos);
904 
905             // Color transformation cmyk -> rgb
906             tmc->r = 255 - (tmc->c + tmc->k);
907             if (tmc->r < 0) tmc->r = 0;
908 
909             tmc->g = 255 - (tmc->m + tmc->k);
910             if (tmc->g < 0) tmc->g = 0;
911 
912             tmc->b = 255 - (tmc->y + tmc->k);
913             if (tmc->b < 0) tmc->b = 0;
914 
915             mcol.append(tmc);
916         }
917 
918     } while (ok == true);
919 
920     kDebug() << "... done" << mcol.count();
921 
922     foreach(t_mycolor* emp, mcol) {
923         printf(" c:%3d m:%3d y:%3d k:%3d   r:%3d g:%3d b:%3d\n",
924                emp->c, emp->m, emp->y, emp->k, emp->r, emp->g, emp->b);
925     }
926 }
927 
928 
929 
930 
931 /******************************************************************************
932  *  function:   readColormap                                                  *
933  ******************************************************************************/
934 void
readView(QTextStream & stream,const QString & instr,t_rc & rc)935 APPLIXSPREADImport::readView(QTextStream &stream, const QString &instr, t_rc &rc)
936 {
937     QString rowcolstr;
938     QString mystr, tabname;
939 
940     kDebug() << "Reading View";
941 
942     tabname = instr;
943 
944     tabname.remove(0, 19);
945     tabname.remove(tabname.length() - 2, 2);
946     kDebug() << "  - Table name:" << tabname;
947 
948     bool ok = true;
949     do {
950         mystr = nextLine(stream);
951 
952         kDebug() << "" << mystr;
953         if (mystr.startsWith("View End, Name:")) ok = false;
954         else {
955             // COLUMN Widths
956             if (mystr.startsWith("View Column Widths")) {
957                 kDebug() << "   - Column Widths";
958                 mystr.remove(0, 20);
959                 kDebug() << "" << mystr;
960 
961                 int  colwidth, icolumn;
962                 char ccolumn;
963 
964                 // loop
965                 QStringList ColumnList;
966                 ColumnList = mystr.split(' ');
967 
968                 for (QStringList::Iterator it = ColumnList.begin(); it != ColumnList.end(); ++it) {
969 
970                     sscanf((*it).toLatin1(), "%c:%d", &ccolumn, &colwidth);
971                     int len = (*it).length();
972                     int pos = (*it).indexOf(':');
973                     (*it).remove(pos, len - pos);
974 
975                     printf("     >%s<- -<%c><%d>  \n", (*it).toLatin1().data(), ccolumn, colwidth);
976 
977                     // Transformat ascii column to int column
978                     icolumn = translateColumnNumber(*it);
979 
980                     //icolumn = ccolumn - 64;
981                     // Translate the column width right from applix to kspread
982                     icolumn = icolumn * 5;
983 
984 
985                     rowcolstr += "  <column width=\"" +
986                                  QString::number(colwidth) +
987                                  "\" column=\"" +
988                                  QString::number(icolumn) +
989                                  "\" >\n"
990                                  "   <format/>\n"
991                                  "  </column>\n";
992                 }
993             }
994 
995             // ROW Heights
996             else if (mystr.startsWith("View Row Heights")) {
997                 kDebug() << "   - Row Heights";
998                 mystr.remove(0, 17);
999                 kDebug() << "" << mystr;
1000 
1001                 int irow, rowheight;
1002 
1003                 // loop
1004                 QStringList RowList;
1005                 RowList = mystr.split(' ');
1006 
1007                 for (QStringList::Iterator it = RowList.begin(); it != RowList.end(); ++it) {
1008                     sscanf((*it).toLatin1(), " %d:%d",
1009                            &irow, &rowheight);
1010                     printf("   row: %2d   height: %2d\n", irow, rowheight);
1011                     if (rowheight > 32768) rowheight -= 32768;
1012                     printf("              height: %2d\n", rowheight);
1013                     rowcolstr += "  <row row=\"" +
1014                                  QString::number(irow) +
1015                                  "\" height=\"" +
1016                                  QString::number(rowheight) +
1017                                  "\" >\n"
1018                                  "   <format/>\n"
1019                                  "  </row>\n";
1020                 }
1021 
1022 
1023             }
1024         } // else != END COLORMAP
1025     } while (ok == true);
1026 
1027     // tabname append to my list
1028     // tabname append to my list
1029     rc.tabname.append(tabname);
1030     rc.rc.append(rowcolstr);
1031 
1032     printf("%s %s\n", tabname.toLatin1().data(),
1033            rowcolstr.toLatin1().data());
1034 
1035     printf("...done \n\n");
1036 }
1037 
1038 
1039 
1040 
1041 
1042 /******************************************************************************
1043  *  function:   filterSHFGBG                                                  *
1044  ******************************************************************************/
1045 void
filterSHFGBG(const QString & it,int * style,int * bgcolor,int * fgcolor)1046 APPLIXSPREADImport::filterSHFGBG(const QString &it, int *style, int *bgcolor,
1047                                  int *fgcolor)
1048 {
1049     QString tmpstr;
1050     int     pos;
1051     int     m2 = 0, m3 = 0;
1052 
1053     // filter SH = Brushstyle Background
1054     pos = it.indexOf("SH");
1055     if (pos > -1) {
1056         tmpstr = it;
1057         if (pos > 0)   tmpstr.remove(0, pos);
1058         pos = sscanf(tmpstr.toLatin1(), "SH%d",
1059                      style);
1060 
1061         printf("style: %d(%d)  ",
1062                *style, pos);
1063     }
1064 
1065 
1066     // filter FG = FGCOLOR
1067     pos = it.indexOf("FG");
1068     if (pos > -1) {
1069         tmpstr = it;
1070         if (pos > 0)   tmpstr.remove(0, pos);
1071         pos = sscanf(tmpstr.toLatin1(), "FG%d",
1072                      fgcolor);
1073         printf("fg: %d(%d)  ",
1074                *fgcolor, pos);
1075         m2 = 1;
1076     }
1077 
1078 
1079     // filter BG = BGCOLOR
1080     pos = it.indexOf("BG");
1081     if (pos > -1) {
1082         tmpstr = it;
1083         if (pos > 0)   tmpstr.remove(0, pos);
1084         pos = sscanf(tmpstr.toLatin1(), "BG%d",
1085                      bgcolor);
1086         printf("bgcolor: %d(%d)  ",
1087                *bgcolor, pos);
1088         m3 = 1;
1089     }
1090 
1091 
1092     printf("\n");
1093 
1094 
1095     // correct the bgcolor to the fgcolor if the background is plain
1096     if ((*style == 8) && (m2 == 1) && (m3 == 0)) {
1097         *bgcolor = *fgcolor;
1098     }
1099 
1100 
1101     // Translate brushstyle to kspread brushstyle
1102     if (*style != 0) {
1103         if (*style ==  1) *style =  0;
1104         else if (*style ==  2) *style =  7;
1105         else if (*style ==  3) *style =  0;
1106         else if (*style ==  4) *style =  4;
1107         else if (*style ==  5) *style =  3;
1108         else if (*style ==  6) *style =  2;
1109         else if (*style ==  7) *style =  0;
1110         else if (*style ==  8) *style =  0;
1111         else if (*style ==  9) *style = 10;
1112         else if (*style == 10) *style =  9;
1113         else if (*style == 11) *style = 11;
1114         else if (*style == 12) *style = 12;
1115         else if (*style == 13) *style = 13;
1116         else if (*style == 14) *style = 14;
1117         else if (*style == 15) *style =  0;
1118         else if (*style == 16) *style =  0;
1119         else if (*style == 17) *style =  0;
1120         else if (*style == 18) *style =  0;
1121         else if (*style == 19) *style =  0;
1122     }
1123 }
1124 
1125 
1126 
1127 /******************************************************************************
1128  *  function:   filterSHFGBG                                                  *
1129  ******************************************************************************/
1130 void
transPenFormat(const QString & it,int * PenWidth,int * PenStyle)1131 APPLIXSPREADImport::transPenFormat(const QString &it, int *PenWidth, int *PenStyle)
1132 {
1133 
1134     if (it[1] == '1') {
1135         *PenWidth = 1;
1136         *PenStyle = 1;
1137     }
1138 
1139     else if (it[1] == '2') {
1140         *PenWidth = 2;
1141         *PenStyle = 1;
1142     }
1143 
1144     else if (it[1] == '3') {
1145         *PenWidth = 3;
1146         *PenStyle = 1;
1147     }
1148 
1149     else if (it[1] == '4') {
1150         *PenWidth = 1;
1151         *PenStyle = 3;
1152     }
1153 
1154     else if (it[1] == '5') {
1155         *PenWidth = 5;
1156         *PenStyle = 1;
1157     }
1158 
1159     printf("frame (w:%d - s:%d) \n", *PenWidth, *PenStyle);
1160 }
1161 
1162 
1163 
1164 
1165 /******************************************************************************
1166  *  function: readHeader                                                       *
1167  ******************************************************************************/
1168 int
readHeader(QTextStream & stream)1169 APPLIXSPREADImport::readHeader(QTextStream &stream)
1170 {
1171     QString mystr;
1172     int     vers[3] = { 0, 0, 0 };
1173     int     rueck;
1174 
1175 
1176     // Read Headline
1177     mystr = nextLine(stream);
1178     rueck = sscanf(mystr.toLatin1(),
1179                    "*BEGIN SPREADSHEETS VERSION=%d/%d ENCODING=%dBIT",
1180                    &vers[0], &vers[1], &vers[2]);
1181     printf("Versions info: %d %d %d\n", vers[0], vers[1], vers[2]);
1182 
1183     // Check the headline
1184     if (rueck <= 0) {
1185         printf("Header not correct - May be it is not an applixspreadsheet file\n");
1186         printf("Headerline: <%s>\n", mystr.toLatin1().data());
1187 
1188         QMessageBox::critical(0L, "Applix spreadsheet header problem",
1189                               QString("The Applix Spreadsheet header is not correct. "
1190                                       "May be it is not an applix spreadsheet file! <BR>"
1191                                       "This is the header line I did read:<BR><B>%1</B>").arg(mystr),
1192                               "Okay");
1193 
1194 
1195         return false;
1196     } else {
1197         return true;
1198     }
1199 }
1200 
1201 
1202 /******************************************************************************
1203  *  function: translateColumnNumber                                           *
1204  ******************************************************************************/
1205 int
translateColumnNumber(const QString & colstr)1206 APPLIXSPREADImport::translateColumnNumber(const QString& colstr)
1207 {
1208     int icol = 0;
1209     const int len = colstr.length();
1210     int p = len - 1;
1211     int x = 1;
1212 
1213     //kDebug() << "len=" << len;
1214     while (p >= 0) {
1215         //kDebug() << "x=" << x << "p=" << p << "char=" << colstr[p].toLatin1();
1216         const char c = colstr[p].toLatin1();
1217         // Upper chars
1218         if ((c >= 'A') && (c <= 'Z')) {
1219             //kDebug() << " UPPER";
1220             icol += ((int)pow((double)x, 26) * (c - 'A' + 1));
1221             ++x;
1222         }
1223         // lower chars
1224         else if ((c >= 'a') && (c <= 'z')) {
1225             //kDebug() << " lower";
1226             icol += ((int)pow((double)x, 26) * (c - 'a' + 1));
1227             ++x;
1228         }
1229         p--;
1230     }
1231 
1232     kDebug() << colstr << "->" << icol;
1233     return icol;
1234 }
1235 
1236 // Converts =SUM(F1,4) into =SUM(F1;4) -- well, plus possible nesting
convertFormula(const QString & input) const1237 QString APPLIXSPREADImport::convertFormula(const QString& input) const
1238 {
1239     // Let me be stupid for now
1240     QString ret = input;
1241     ret.replace(',', ';');
1242     return ret;
1243 }
1244 
1245 #include "applixspreadimport.moc"
1246