1 // SciTE - Scintilla based Text Editor
2 /** @file ExportTEX.cxx
3  ** Export the current document to TEX.
4  **/
5 // Copyright 1998-2006 by Neil Hodgson <neilh@scintilla.org>
6 // The License.txt file describes the conditions under which this software may be distributed.
7 
8 #include <cstddef>
9 #include <cstdlib>
10 #include <cstdint>
11 #include <cstring>
12 #include <cstdio>
13 #include <ctime>
14 
15 #include <string>
16 #include <string_view>
17 #include <vector>
18 #include <map>
19 #include <set>
20 #include <memory>
21 #include <chrono>
22 #include <sstream>
23 #include <atomic>
24 #include <mutex>
25 
26 #include <fcntl.h>
27 #include <sys/stat.h>
28 
29 #include "ILexer.h"
30 
31 #include "ScintillaTypes.h"
32 #include "ScintillaCall.h"
33 
34 #include "GUI.h"
35 #include "ScintillaWindow.h"
36 
37 #include "StringList.h"
38 #include "StringHelpers.h"
39 #include "FilePath.h"
40 #include "StyleDefinition.h"
41 #include "PropSetFile.h"
42 #include "StyleWriter.h"
43 #include "Extender.h"
44 #include "SciTE.h"
45 #include "JobQueue.h"
46 #include "Cookie.h"
47 #include "Worker.h"
48 #include "MatchMarker.h"
49 #include "SciTEBase.h"
50 
51 //---------- Save to TeX ----------
52 
getTexRGB(char * texcolor,const char * stylecolor)53 static char *getTexRGB(char *texcolor, const char *stylecolor) noexcept {
54 	//texcolor[rgb]{0,0.5,0}{....}
55 	const double rf = IntFromHexByte(stylecolor + 1) / 256.0;
56 	const double gf = IntFromHexByte(stylecolor + 3) / 256.0;
57 	const double bf = IntFromHexByte(stylecolor + 5) / 256.0;
58 	// avoid breakage due to locale setting
59 	const int r = static_cast<int>(rf * 10 + 0.5);
60 	const int g = static_cast<int>(gf * 10 + 0.5);
61 	const int b = static_cast<int>(bf * 10 + 0.5);
62 	sprintf(texcolor, "%d.%d, %d.%d, %d.%d", r / 10, r % 10, g / 10, g % 10, b / 10, b % 10);
63 	return texcolor;
64 }
65 
66 #define CHARZ ('z' - 'b')
texStyle(int style)67 static char *texStyle(int style) noexcept {
68 	static char buf[10];
69 	int i = 0;
70 	do {
71 		buf[i++] = static_cast<char>('a' + (style % CHARZ));
72 		style /= CHARZ;
73 	} while (style > 0);
74 	buf[i] = 0;
75 	return buf;
76 }
77 
defineTexStyle(const StyleDefinition & style,FILE * fp,int istyle)78 static void defineTexStyle(const StyleDefinition &style, FILE *fp, int istyle) {
79 	int closing_brackets = 2;
80 	char rgb[200] = "";
81 	fprintf(fp, "\\newcommand{\\scite%s}[1]{\\noindent{\\ttfamily{", texStyle(istyle));
82 	if (style.italics) {
83 		fputs("\\textit{", fp);
84 		closing_brackets++;
85 	}
86 	if (style.IsBold()) {
87 		fputs("\\textbf{", fp);
88 		closing_brackets++;
89 	}
90 	if (style.fore.length()) {
91 		fprintf(fp, "\\textcolor[rgb]{%s}{", getTexRGB(rgb, style.fore.c_str()));
92 		closing_brackets++;
93 	}
94 	if (style.back.length()) {
95 		fprintf(fp, "\\colorbox[rgb]{%s}{", getTexRGB(rgb, style.back.c_str()));
96 		closing_brackets++;
97 	}
98 	fputs("#1", fp);
99 	for (int i = 0; i <= closing_brackets; i++) {
100 		fputc('}', fp);
101 	}
102 	fputc('\n', fp);
103 }
104 
SaveToTEX(const FilePath & saveName)105 void SciTEBase::SaveToTEX(const FilePath &saveName) {
106 	RemoveFindMarks();
107 	wEditor.ColouriseAll();
108 	int tabSize = props.GetInt("tabsize");
109 	if (tabSize == 0)
110 		tabSize = 4;
111 
112 	const SA::Position lengthDoc = LengthDocument();
113 	TextReader acc(wEditor);
114 	bool styleIsUsed[StyleMax + 1] = {};
115 
116 	const int titleFullPath = props.GetInt("export.tex.title.fullpath", 0);
117 
118 	for (SA::Position pos = 0; pos < lengthDoc; pos++) {	// check the used styles
119 		styleIsUsed[acc.StyleAt(pos)] = true;
120 	}
121 	styleIsUsed[StyleDefault] = true;
122 
123 	FILE *fp = saveName.Open(GUI_TEXT("wt"));
124 	bool failedWrite = fp == nullptr;
125 	if (fp) {
126 		fputs("\\documentclass[a4paper]{article}\n"
127 		      "\\usepackage[a4paper,margin=2cm]{geometry}\n"
128 		      "\\usepackage[T1]{fontenc}\n"
129 		      "\\usepackage{color}\n"
130 		      "\\usepackage{alltt}\n"
131 		      "\\usepackage{times}\n"
132 		      "\\setlength{\\fboxsep}{0pt}\n", fp);
133 
134 		for (int istyle = 0; istyle < StyleMax; istyle++) {      // get keys
135 			if (styleIsUsed[istyle]) {
136 				StyleDefinition sd = StyleDefinitionFor(istyle);
137 				defineTexStyle(sd, fp, istyle); // writeout style macroses
138 			}
139 		}
140 
141 		fputs("\\begin{document}\n\n", fp);
142 		fprintf(fp, "Source File: %s\n\n\\noindent\n\\small{\n",
143 			titleFullPath ? filePath.AsUTF8().c_str() : filePath.Name().AsUTF8().c_str());
144 
145 		int styleCurrent = acc.StyleAt(0);
146 
147 		fprintf(fp, "\\scite%s{", texStyle(styleCurrent));
148 
149 		int lineIdx = 0;
150 
151 		for (SA::Position i = 0; i < lengthDoc; i++) { //here process each character of the document
152 			const char ch = acc[i];
153 			const int style = acc.StyleAt(i);
154 
155 			if (style != styleCurrent) { //new style?
156 				fprintf(fp, "}\\scite%s{", texStyle(style));
157 				styleCurrent = style;
158 			}
159 
160 			switch (ch) {   //write out current character.
161 			case '\t': {
162 					const int ts = tabSize - (lineIdx % tabSize);
163 					lineIdx += ts - 1;
164 					fprintf(fp, "\\hspace*{%dem}", ts);
165 					break;
166 				}
167 			case '\\':
168 				fputs("{\\textbackslash}", fp);
169 				break;
170 			case '>':
171 			case '<':
172 			case '@':
173 				fprintf(fp, "$%c$", ch);
174 				break;
175 			case '{':
176 			case '}':
177 			case '^':
178 			case '_':
179 			case '&':
180 			case '$':
181 			case '#':
182 			case '%':
183 			case '~':
184 				fprintf(fp, "\\%c", ch);
185 				break;
186 			case '\r':
187 			case '\n':
188 				lineIdx = -1;	// Because incremented below
189 				if (ch == '\r' && acc[i + 1] == '\n')
190 					i++;	// Skip the LF
191 				styleCurrent = acc.StyleAt(i + 1);
192 				fprintf(fp, "} \\\\\n\\scite%s{", texStyle(styleCurrent));
193 				break;
194 			case ' ':
195 				if (acc[i + 1] == ' ') {
196 					fputs("{\\hspace*{1em}}", fp);
197 				} else {
198 					fputc(' ', fp);
199 				}
200 				break;
201 			default:
202 				fputc(ch, fp);
203 			}
204 			lineIdx++;
205 		}
206 		fputs("}\n} %end small\n\n\\end{document}\n", fp); //close last empty style macros and document too
207 		if (fclose(fp) != 0) {
208 			failedWrite = true;
209 		}
210 	}
211 	if (failedWrite) {
212 		FailedSaveMessageBox(saveName);
213 	}
214 }
215