1 /*
2 Large portions of this file were adapted from the SciTE text
3 editor source code.
4
5 SciTE is copyright 1998-2010 by Neil Hodgson <neilh@scintilla.org>
6 and is released under the following license:
7
8 All Rights Reserved
9
10 Permission to use, copy, modify, and distribute this software and its
11 documentation for any purpose and without fee is hereby granted,
12 provided that the above copyright notice appear in all copies and that
13 both that copyright notice and this permission notice appear in
14 supporting documentation.
15
16 NEIL HODGSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
17 SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
18 AND FITNESS, IN NO EVENT SHALL NEIL HODGSON BE LIABLE FOR ANY
19 SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
21 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
22 TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
23 OR PERFORMANCE OF THIS SOFTWARE.
24 */
25
26
27 #include <stdio.h>
28 #include <string.h>
29
30 #include <fx.h>
31 #include <FX88591Codec.h>
32
33 #include "appname.h"
34 #include "scidoc.h"
35 #include "prefs.h"
36 #include "lang.h"
37 #include "export.h"
38
39 #define CharAt(where) sci->sendMessage(SCI_GETCHARAT, where, 0)
40
41 //---------- Save to HTML ----------
42
SaveToHTML(SciDoc * sci,FILE * fp)43 void SaveToHTML(SciDoc*sci, FILE *fp )
44 {
45 sci->sendMessage(SCI_COLOURISE, 0, -1);
46 int tabSize = 4;
47 int lengthDoc = sci->GetTextLength();
48
49
50 bool styleIsUsed[STYLE_MAX + 1];
51 bool onlyStylesUsed=true;
52 if (onlyStylesUsed) {
53 int i;
54 for (i = 0; i <= STYLE_MAX; i++) {
55 styleIsUsed[i] = false;
56 }
57 // check the used styles
58 for (i = 0; i < lengthDoc; i++) {
59 styleIsUsed[sci->sendMessage(SCI_GETSTYLEAT,i,0) & 0x7F] = true;
60 }
61 } else {
62 for (int i = 0; i <= STYLE_MAX; i++) {
63 styleIsUsed[i] = true;
64 }
65 }
66 styleIsUsed[STYLE_DEFAULT] = true;
67
68 fputs( "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \
69 \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n", fp);
70
71 fputs("<html xmlns=\"http://www.w3.org/1999/xhtml\">\n", fp);
72 fputs("<head>\n", fp);
73 fprintf(fp, "<title>%s</title>\n", FXPath::name(sci->Filename()).text());
74 // Probably not used by robots, but making a little advertisement for those looking
75 // at the source code doesn't hurt...
76 fputs("<meta name=\"Generator\" content=\"" APP_NAME " HTML export\" />\n", fp);
77 if ( sci->sendMessage(SCI_GETCODEPAGE,0,0) == SC_CP_UTF8) {
78 fputs("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />\n", fp);
79 }
80 fputs("<style type=\"text/css\">\n", fp);
81
82 char* bgColour=Settings::globalStyle()->bg;
83 LangStyle* ls = sci->getLanguage();
84
85 for (int istyle = 0; istyle <= STYLE_MAX; istyle++) {
86 if ((istyle > STYLE_DEFAULT) && (istyle <= STYLE_LASTPREDEFINED)) { continue; }
87 if (styleIsUsed[istyle]) {
88 StyleDef*sd=GetStyleFromId(ls?ls->styles:Settings::globalStyle(), istyle);
89 if (!sd) { sd=Settings::globalStyle(); }
90 if (istyle == STYLE_DEFAULT) {
91 fprintf(fp, "span {\n");
92 } else {
93 fprintf(fp, ".S%0d {\n", istyle);
94 }
95 if (sd->style & Italic) { fprintf(fp, " font-style: italic;\n"); }
96 if (sd->style & Bold) { fprintf(fp, " font-weight: bold;\n"); }
97 if (sd->fg[0]) {
98 fprintf(fp, " color: %s;\n", sd->fg);
99 } else if (istyle == STYLE_DEFAULT) {
100 fprintf(fp, " color: #000000;\n");
101 }
102 if (sd->bg[0]) {
103 if (istyle != STYLE_DEFAULT && (strcmp(bgColour,sd->bg)!=0)) {
104 fprintf(fp, " background: %s;\n", sd->bg);
105 fprintf(fp, " text-decoration: inherit;\n");
106 }
107 }
108 fprintf(fp, "}\n");
109 }
110 }
111 fputs("</style>\n", fp);
112 fputs("</head>\n", fp);
113 if (bgColour[0]) {
114 fprintf(fp, "<body bgcolor=\"%s\">\n", bgColour);
115 } else {
116 fputs("<body>\n", fp);
117 }
118
119 int styleCurrent = sci->sendMessage(SCI_GETSTYLEAT,0,0);
120 bool inStyleSpan = false;
121 bool inFoldSpan = false;
122 // Global span for default attributes
123 fputs("<pre>", fp);
124
125 if (styleIsUsed[styleCurrent]) {
126 fprintf(fp, "<span class=\"S%0d\">", styleCurrent);
127 inStyleSpan = true;
128 }
129 // Else, this style has no definition (beside default one):
130 // no span for it, except the global one
131
132 int column = 0;
133 for (int i = 0; i < lengthDoc; i++) {
134 char ch = sci->sendMessage(SCI_GETCHARAT,i,0);
135 int style = sci->sendMessage(SCI_GETSTYLEAT,i,0);
136
137 if (style != styleCurrent) {
138 if (inStyleSpan) {
139 fputs("</span>", fp);
140 inStyleSpan = false;
141 }
142 if (ch != '\r' && ch != '\n') { // No need of a span for the EOL
143 if (styleIsUsed[style]) {
144 fprintf(fp, "<span class=\"S%0d\">", style);
145 inStyleSpan = true;
146 }
147 styleCurrent = style;
148 }
149 }
150 if (ch == ' ') {
151 fputc(' ', fp);
152 column++;
153
154 } else if (ch == '\t') {
155 int ts = tabSize - (column % tabSize);
156 for (int itab = 0; itab < ts; itab++) { fputc(' ', fp); }
157 column += ts;
158 } else if (ch == '\r' || ch == '\n') {
159 if (inStyleSpan) {
160 fputs("</span>", fp);
161 inStyleSpan = false;
162 }
163 if (inFoldSpan) {
164 fputs("</span>", fp);
165 inFoldSpan = false;
166 }
167 if ((ch == '\r') && (sci->sendMessage(SCI_GETCHARAT, i + 1, 0) == '\n')) {
168 i++; // CR+LF line ending, skip the "extra" EOL char
169 }
170 column = 0;
171 styleCurrent = sci->sendMessage(SCI_GETSTYLEAT, i + 1, 0);
172 fputc('\n', fp);
173 if (styleIsUsed[styleCurrent] && (CharAt(i+1) != '\r') && (CharAt(i+1) != '\n')) {
174 // We know it's the correct next style,
175 // but no (empty) span for an empty line
176 fprintf(fp, "<span class=\"S%0d\">", styleCurrent);
177 inStyleSpan = true;
178 }
179 } else {
180 switch (ch) {
181 case '<':
182 fputs("<", fp);
183 break;
184 case '>':
185 fputs(">", fp);
186 break;
187 case '&':
188 fputs("&", fp);
189 break;
190 default:
191 fputc(ch, fp);
192 }
193 column++;
194 }
195 }
196 if (inStyleSpan) {
197 fputs("</span>", fp);
198 }
199 fputs("</pre>", fp);
200 fputs("\n</body>\n</html>\n", fp);
201 }
202
203 class Point {
204 public:
205 int x;
206 int y;
Point(int x_=0,int y_=0)207 explicit Point(int x_=0, int y_=0) : x(x_), y(y_) {}
208 static Point FromLong(long lpoint);
209 };
210
211 class PRectangle {
212 public:
213 int left;
214 int top;
215 int right;
216 int bottom;
PRectangle(int left_=0,int top_=0,int right_=0,int bottom_=0)217 PRectangle(int left_=0, int top_=0, int right_=0, int bottom_ = 0) :
218 left(left_), top(top_), right(right_), bottom(bottom_) {
219 }
operator ==(PRectangle & rc)220 bool operator==(PRectangle &rc) {
221 return (rc.left == left) && (rc.right == right) &&
222 (rc.top == top) && (rc.bottom == bottom);
223 }
Contains(Point pt)224 bool Contains(Point pt) {
225 return (pt.x >= left) && (pt.x <= right) &&
226 (pt.y >= top) && (pt.y <= bottom);
227 }
Contains(PRectangle rc)228 bool Contains(PRectangle rc) {
229 return (rc.left >= left) && (rc.right <= right) &&
230 (rc.top >= top) && (rc.bottom <= bottom);
231 }
Intersects(PRectangle other)232 bool Intersects(PRectangle other) {
233 return (right > other.left) && (left < other.right) &&
234 (bottom > other.top) && (top < other.bottom);
235 }
Move(int xDelta,int yDelta)236 void Move(int xDelta, int yDelta) {
237 left += xDelta;
238 top += yDelta;
239 right += xDelta;
240 bottom += yDelta;
241 }
Width()242 int Width() { return right - left; }
Height()243 int Height() { return bottom - top; }
Empty()244 bool Empty() {
245 return (Height() <= 0) || (Width() <= 0);
246 }
247 };
248
249
IntFromHexDigit(int ch)250 static int IntFromHexDigit(int ch) {
251 if ((ch >= '0') && (ch <= '9')) {
252 return ch - '0';
253 } else if (ch >= 'A' && ch <= 'F') {
254 return ch - 'A' + 10;
255 } else if (ch >= 'a' && ch <= 'f') {
256 return ch - 'a' + 10;
257 } else {
258 return 0;
259 }
260 }
261
IntFromHexByte(const char * hexByte)262 int IntFromHexByte(const char *hexByte) {
263 return IntFromHexDigit(hexByte[0]) * 16 + IntFromHexDigit(hexByte[1]);
264 }
265
266 //---------- Save to PDF ----------
267
268 /*
269 PDF Exporter. Status: Beta
270 Contributed by Ahmad M. Zawawi <zeus_go64@hotmail.com>
271 Modifications by Darren Schroeder Feb 22, 2003; Philippe Lhoste 2003-10
272 Overhauled by Kein-Hong Man 2003-11
273
274 This exporter is meant to be small and simple; users are expected to
275 use other methods for heavy-duty formatting. PDF elements marked with
276 "PDF1.4Ref" states where in the PDF 1.4 Reference Spec (the PDF file of
277 which is freely available from Adobe) the particular element can be found.
278
279 Possible TODOs that will probably not be implemented: full styling,
280 optimization, font substitution, compression, character set encoding.
281 */
282 #define PDF_TAB_DEFAULT 8
283 #define PDF_FONT_DEFAULT 1 // Helvetica
284 #define PDF_FONTSIZE_DEFAULT 10
285 #define PDF_SPACING_DEFAULT 1.2
286 #define PDF_HEIGHT_DEFAULT 792 // Letter
287 #define PDF_WIDTH_DEFAULT 612
288 #define PDF_MARGIN_DEFAULT 72 // 1.0"
289 #define PDF_ENCODING "WinAnsiEncoding"
290
291 struct PDFStyle {
292 char fore[24];
293 int font;
294 };
295
296 static const char *PDFfontNames[] = {
297 "Courier", "Courier-Bold", "Courier-Oblique", "Courier-BoldOblique",
298 "Helvetica", "Helvetica-Bold", "Helvetica-Oblique", "Helvetica-BoldOblique",
299 "Times-Roman", "Times-Bold", "Times-Italic", "Times-BoldItalic"
300 };
301
302 // ascender, descender aligns font origin point with page
303 static short PDFfontAscenders[] = { 629, 718, 699 };
304 static short PDFfontDescenders[] = { 157, 207, 217 };
305 static short PDFfontWidths[] = { 600, 0, 0 };
306
getPDFRGB(char * pdfcolour,const char * stylecolour)307 static inline void getPDFRGB(char* pdfcolour, const char* stylecolour) {
308 // grab colour components (max string length produced = 18)
309 for (int i = 1; i < 6; i += 2) {
310 char val[20];
311 // 3 decimal places for enough dynamic range
312 int c = (IntFromHexByte(stylecolour + i) * 1000 + 127) / 255;
313 if (c == 0 || c == 1000) { // optimise
314 sprintf(val, "%d ", c / 1000);
315 } else {
316 sprintf(val, "0.%03d ", c);
317 }
318 strcat(pdfcolour, val);
319 }
320 }
321
SaveToPDF(SciDoc * sci,FILE * fp)322 void SaveToPDF(SciDoc*sci, FILE *fp)
323 {
324 // This class conveniently handles the tracking of PDF objects
325 // so that the cross-reference table can be built (PDF1.4Ref(p39))
326 // All writes to fp passes through a PDFObjectTracker object.
327 class PDFObjectTracker {
328 private:
329 FILE *fp;
330 int *offsetList, tableSize;
331 public:
332 int index;
333 PDFObjectTracker(FILE *fp_) {
334 fp = fp_;
335 tableSize = 100;
336 offsetList = new int[tableSize];
337 index = 1;
338 }
339 ~PDFObjectTracker() {
340 delete []offsetList;
341 }
342 void write(const char *objectData) {
343 unsigned int length = strlen(objectData);
344 // note binary write used, open with "wb"
345 fwrite(objectData, sizeof(char), length, fp);
346 }
347 void write(int objectData) {
348 char val[20];
349 sprintf(val, "%d", objectData);
350 write(val);
351 }
352 // returns object number assigned to the supplied data
353 int add(const char *objectData) {
354 // resize xref offset table if too small
355 if (index > tableSize) {
356 int newSize = tableSize * 2;
357 int *newList = new int[newSize];
358 for (int i = 0; i < tableSize; i++) {
359 newList[i] = offsetList[i];
360 }
361 delete []offsetList;
362 offsetList = newList;
363 tableSize = newSize;
364 }
365 // save offset, then format and write object
366 offsetList[index - 1] = ftell(fp);
367 write(index);
368 write(" 0 obj\n");
369 write(objectData);
370 write("endobj\n");
371 return index++;
372 }
373 // builds xref table, returns file offset of xref table
374 int xref() {
375 char val[32];
376 // xref start index and number of entries
377 int xrefStart = ftell(fp);
378 write("xref\n0 ");
379 write(index);
380 // a xref entry *must* be 20 bytes long (PDF1.4Ref(p64))
381 // so extra space added; also the first entry is special
382 write("\n0000000000 65535 f \n");
383 for (int i = 0; i < index - 1; i++) {
384 sprintf(val, "%010d 00000 n \n", offsetList[i]);
385 write(val);
386 }
387 return xrefStart;
388 }
389 };
390
391 // Object to manage line and page rendering. Apart from startPDF, endPDF
392 // everything goes in via add() and nextLine() so that line formatting
393 // and pagination can be done properly.
394 class PDFRender {
395 private:
396 bool pageStarted;
397 bool firstLine;
398 int pageCount;
399 int pageContentStart;
400 double xPos, yPos; // position tracking for line wrapping
401 FXString pageData; // holds PDF stream contents
402 FXString segment; // character data
403 char *segStyle; // style of segment
404 bool justWhiteSpace;
405 int styleCurrent, stylePrev;
406 double leading;
407 char *buffer;
408 public:
409 PDFObjectTracker *oT;
410 PDFStyle *style;
411 int fontSize; // properties supplied by user
412 int fontSet;
413 int pageWidth, pageHeight;
414 PRectangle pageMargin;
415 //
416 PDFRender() {
417 pageStarted = false;
418 pageCount = 0;
419 style = NULL;
420 buffer = new char[250];
421 segStyle = new char[100];
422 }
423 ~PDFRender() {
424 if (style) { delete []style; }
425 delete []buffer;
426 delete []segStyle;
427 }
428 //
429 double fontToPoints(int thousandths) {
430 return (double)fontSize * thousandths / 1000.0;
431 }
432 void setStyle(char *buff, int style_) {
433 int styleNext = style_;
434 if (style_ == -1) { styleNext = styleCurrent; }
435 *buff = '\0';
436 if (styleNext != styleCurrent || style_ == -1) {
437 if (style[styleCurrent].font != style[styleNext].font
438 || style_ == -1) {
439 sprintf(buff, "/F%d %d Tf ",
440 style[styleNext].font + 1, fontSize);
441 }
442 if (strcmp(style[styleCurrent].fore, style[styleNext].fore) != 0
443 || style_ == -1) {
444 strcat(buff, style[styleNext].fore);
445 strcat(buff, "rg ");
446 }
447 }
448 }
449 //
450 void startPDF() {
451 if (fontSize <= 0) {
452 fontSize = PDF_FONTSIZE_DEFAULT;
453 }
454 // leading is the term for distance between lines
455 leading = fontSize * PDF_SPACING_DEFAULT;
456 // sanity check for page size and margins
457 int pageWidthMin = (int)leading + pageMargin.left + pageMargin.right;
458 if (pageWidth < pageWidthMin) {
459 pageWidth = pageWidthMin;
460 }
461 int pageHeightMin = (int)leading + pageMargin.top + pageMargin.bottom;
462 if (pageHeight < pageHeightMin) {
463 pageHeight = pageHeightMin;
464 }
465 // start to write PDF file here (PDF1.4Ref(p63))
466 // ASCII>127 characters to indicate binary-possible stream
467 oT->write("%PDF-1.3\n%\267\276\255\252\n");
468 styleCurrent = STYLE_DEFAULT;
469
470 // build objects for font resources; note that font objects are
471 // *expected* to start from index 1 since they are the first objects
472 // to be inserted (PDF1.4Ref(p317))
473 for (int i = 0; i < 4; i++) {
474 sprintf(buffer, "<</Type/Font/Subtype/Type1"
475 "/Name/F%d/BaseFont/%s/Encoding/"
476 PDF_ENCODING
477 ">>\n", i + 1,
478 PDFfontNames[fontSet * 4 + i]);
479 oT->add(buffer);
480 }
481 pageContentStart = oT->index;
482 }
483 void endPDF() {
484 if (pageStarted) { // flush buffers
485 endPage();
486 }
487 // refer to all used or unused fonts for simplicity
488 int resourceRef = oT->add(
489 "<</ProcSet[/PDF/Text]\n"
490 "/Font<</F1 1 0 R/F2 2 0 R/F3 3 0 R"
491 "/F4 4 0 R>> >>\n");
492 // create all the page objects (PDF1.4Ref(p88))
493 // forward reference pages object; calculate its object number
494 int pageObjectStart = oT->index;
495 int pagesRef = pageObjectStart + pageCount;
496 for (int i = 0; i < pageCount; i++) {
497 sprintf(buffer, "<</Type/Page/Parent %d 0 R\n"
498 "/MediaBox[ 0 0 %d %d"
499 "]\n/Contents %d 0 R\n"
500 "/Resources %d 0 R\n>>\n",
501 pagesRef, pageWidth, pageHeight,
502 pageContentStart + i, resourceRef);
503 oT->add(buffer);
504 }
505 // create page tree object (PDF1.4Ref(p86))
506 pageData = "<</Type/Pages/Kids[\n";
507 for (int j = 0; j < pageCount; j++) {
508 sprintf(buffer, "%d 0 R\n", pageObjectStart + j);
509 pageData += buffer;
510 }
511 sprintf(buffer, "]/Count %d\n>>\n", pageCount);
512 pageData += buffer;
513 oT->add(pageData.text());
514 // create catalog object (PDF1.4Ref(p83))
515 sprintf(buffer, "<</Type/Catalog/Pages %d 0 R >>\n", pagesRef);
516 int catalogRef = oT->add(buffer);
517 // append the cross reference table (PDF1.4Ref(p64))
518 int xref = oT->xref();
519 // end the file with the trailer (PDF1.4Ref(p67))
520 sprintf(buffer, "trailer\n<< /Size %d /Root %d 0 R\n>>"
521 "\nstartxref\n%d\n%%%%EOF\n",
522 oT->index, catalogRef, xref);
523 oT->write(buffer);
524 }
525 void add(char ch, int style_) {
526 if (!pageStarted) {
527 startPage();
528 }
529 // get glyph width (TODO future non-monospace handling)
530 double glyphWidth = fontToPoints(PDFfontWidths[fontSet]);
531 xPos += glyphWidth;
532 // if cannot fit into a line, flush, wrap to next line
533 if (xPos > pageWidth - pageMargin.right) {
534 nextLine();
535 xPos += glyphWidth;
536 }
537 // if different style, then change to style
538 if (style_ != styleCurrent) {
539 flushSegment();
540 // output code (if needed) for new style
541 setStyle(segStyle, style_);
542 stylePrev = styleCurrent;
543 styleCurrent = style_;
544 }
545 // escape these characters
546 if (ch == ')' || ch == '(' || ch == '\\') {
547 segment += '\\';
548 }
549 if (ch != ' ') { justWhiteSpace = false; }
550 segment += ch; // add to segment data
551 }
552 void flushSegment() {
553 if (segment.length() > 0) {
554 if (justWhiteSpace) { // optimise
555 styleCurrent = stylePrev;
556 } else {
557 pageData += segStyle;
558 }
559 pageData += "(";
560 pageData += segment;
561 pageData += ")Tj\n";
562 }
563 segment.clear();
564 *segStyle = '\0';
565 justWhiteSpace = true;
566 }
567 void startPage() {
568 pageStarted = true;
569 firstLine = true;
570 pageCount++;
571 double fontAscender = fontToPoints(PDFfontAscenders[fontSet]);
572 yPos = pageHeight - pageMargin.top - fontAscender;
573 // start a new page
574 sprintf(buffer, "BT 1 0 0 1 %d %d Tm\n",
575 pageMargin.left, (int)yPos);
576 // force setting of initial font, colour
577 setStyle(segStyle, -1);
578 strcat(buffer, segStyle);
579 pageData = buffer;
580 xPos = pageMargin.left;
581 segment.clear();
582 flushSegment();
583 }
584 void endPage() {
585 pageStarted = false;
586 flushSegment();
587 // build actual text object; +3 is for "ET\n"
588 // PDF1.4Ref(p38) EOL marker preceding endstream not counted
589 char *textObj = new char[pageData.length() + 100];
590 // concatenate stream within the text object
591 sprintf(textObj, "<</Length %d>>\nstream\n%s"
592 "ET\nendstream\n",
593 static_cast<int>(pageData.length() - 1 + 3),
594 pageData.text());
595 oT->add(textObj);
596 delete []textObj;
597 }
598 void nextLine() {
599 if (!pageStarted) {
600 startPage();
601 }
602 xPos = pageMargin.left;
603 flushSegment();
604 // PDF follows cartesian coords, subtract -> down
605 yPos -= leading;
606 double fontDescender = fontToPoints(PDFfontDescenders[fontSet]);
607 if (yPos < pageMargin.bottom + fontDescender) {
608 endPage();
609 startPage();
610 return;
611 }
612 if (firstLine) {
613 // avoid breakage due to locale setting
614 int f = (int)(leading * 10 + 0.5);
615 sprintf(buffer, "0 -%d.%d TD\n", f / 10, f % 10);
616 firstLine = false;
617 } else {
618 sprintf(buffer, "T*\n");
619 }
620 pageData += buffer;
621 }
622 };
623 PDFRender pr;
624
625 sci->sendMessage(SCI_COLOURISE, 0, -1);
626 int tabSize = PDF_TAB_DEFAULT;
627
628 pr.fontSize = 0;
629
630 FXString propItem = "Courier";
631 pr.fontSet = PDF_FONT_DEFAULT;
632 if (propItem.length()) {
633 if (propItem == "Courier")
634 pr.fontSet = 0;
635 else if (propItem == "Helvetica")
636 pr.fontSet = 1;
637 else if (propItem == "Times")
638 pr.fontSet = 2;
639 }
640
641 pr.pageWidth = PDF_WIDTH_DEFAULT;
642 pr.pageHeight = PDF_HEIGHT_DEFAULT;
643 pr.pageMargin.left = PDF_MARGIN_DEFAULT;
644 pr.pageMargin.right = PDF_MARGIN_DEFAULT;
645 pr.pageMargin.top = PDF_MARGIN_DEFAULT;
646 pr.pageMargin.bottom = PDF_MARGIN_DEFAULT;
647
648 // collect all styles available for that 'language'
649 // or the default style if no language is available...
650 pr.style = new PDFStyle[STYLE_MAX + 1];
651
652 LangStyle* ls = sci->getLanguage();
653 if (ls) {
654 StyleDef* sd;
655 int i;
656 for (i=0; (i<=STYLE_MAX); i++) { // get keys
657 sd=GetStyleFromId(ls->styles, i);
658 if (!sd) { sd=Settings::globalStyle(); }
659
660 pr.style[i].font = 0;
661 if (sd->style & Italic) { pr.style[i].font |= 2; }
662 if (sd->style & Bold) { pr.style[i].font |= 1; }
663 pr.style[i].fore[0] = '\0';
664 if (sd->fg[0]) {
665 getPDFRGB(pr.style[i].fore, sd->fg);
666 } else if (i == STYLE_DEFAULT) {
667 strcpy(pr.style[i].fore, "0 0 0 ");
668 }
669 pr.fontSize = PDF_FONTSIZE_DEFAULT;
670 }
671 // patch in default foregrounds
672 for (int j = 0; j <= STYLE_MAX; j++) {
673 if (pr.style[j].fore[0] == '\0') {
674 strcpy(pr.style[j].fore, pr.style[STYLE_DEFAULT].fore);
675 }
676 }
677 } else {
678 for (int i = 0; i <= STYLE_MAX; i++) {
679 strcpy(pr.style[i].fore, "0 0 0 ");
680 }
681 }
682
683
684 // initialise PDF rendering
685 PDFObjectTracker ot(fp);
686 pr.oT = &ot;
687 pr.startPDF();
688
689 // do here all the writing
690 int lengthDoc = sci->GetTextLength();
691 int lineIndex = 0;
692
693 if (!lengthDoc) { // enable zero length docs
694 pr.nextLine();
695 } else {
696 FX88591Codec *codec = sci->GetUTF8() ? new FX88591Codec() : NULL;
697 for (int i = 0; i < lengthDoc; i++) {
698 char ch = sci->sendMessage(SCI_GETCHARAT,i,0);
699 int style = sci->sendMessage(SCI_GETSTYLEAT,i,0);
700
701 if (ch == '\t') {
702 // expand tabs
703 int ts = tabSize - (lineIndex % tabSize);
704 lineIndex += ts;
705 for (; ts; ts--) { // add ts count of spaces
706 pr.add(' ', style); // add spaces
707 }
708 } else if (ch == '\r' || ch == '\n') {
709 if (ch == '\r' && sci->sendMessage(SCI_GETCHARAT,i+1,0) == '\n') {
710 i++;
711 }
712 // close and begin a newline...
713 pr.nextLine();
714 lineIndex = 0;
715 } else {
716 // write the character normally...
717 if (codec) {
718 long charlen=sci->sendMessage(SCI_POSITIONAFTER,i,0)-i;
719 if ((charlen>1)&&(charlen<=8)) {
720 // PDF doesn't like UTF-8, try conversion to single-byte ISO-8859
721 char utf[9]="\0";
722 char asc[2]="\0";
723 Sci_TextRange tr;
724 tr.chrg.cpMin=i;
725 tr.chrg.cpMax=i+charlen;
726 tr.lpstrText=utf;
727 sci->sendMessage(SCI_GETTEXTRANGE, 0, reinterpret_cast<long>(&tr));
728 codec->utf2mb(asc, sizeof(asc), tr.lpstrText, charlen);
729 if (asc[0]&&!asc[1]) {
730 // Conversion succeeded: use our single byte and move past the multi-bytes
731 ch=asc[0];
732 i+=charlen-1;
733 }
734 }
735 }
736 pr.add(ch, style);
737 lineIndex++;
738 }
739 }
740 if (codec) { delete codec; }
741 }
742
743 // write required stuff and close the PDF file
744 pr.endPDF();
745
746 }
747
748