-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathProgram.cs
287 lines (241 loc) · 10.5 KB
/
Program.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
// System
using System.Drawing;
using System.Diagnostics;
using System.Runtime.InteropServices;
// Google Cloud Translation API
using Google.Cloud.Translation.V2;
// Add the following assembly references to the project for PowerPoint automation
//
// C:\Program Files (x86)\Microsoft Visual Studio\Shared\Visual Studio Tools for Office\PIA\Office15\Office.dll
// C:\Program Files (x86)\Microsoft Visual Studio\Shared\Visual Studio Tools for Office\PIA\Office15\Microsoft.Office.Interop.Excel.dll
// C:\Program Files (x86)\Microsoft Visual Studio\Shared\Visual Studio Tools for Office\PIA\Office15\Microsoft.Office.Interop.PowerPoint.dll
//
// Repair Office 365 (Online mode) in case of any COM errors
//
using Microsoft.Office.Core;
using Microsoft.Office.Interop.Excel;
using Excel = Microsoft.Office.Interop.Excel;
using PowerPoint = Microsoft.Office.Interop.PowerPoint;
namespace PPtrans
{
internal class Program
{
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
[DllImport("user32.dll")]
public static extern bool SetForegroundWindow(IntPtr WindowHandle);
static void Main(string[] args)
{
try
{
// Run in a safe context
MainSafe(args);
}
catch (Exception e)
{
// Show error message
Console.WriteLine(e.Message);
Console.WriteLine(e.StackTrace);
}
finally
{
// Kill orphaned processes
Kill("EXCEL", "");
Kill("POWERPNT", "PowerPoint");
Console.ReadLine();
}
}
/// <summary>
/// Defines the entry point of the application (Safe Context).
/// </summary>
/// <param name="args">The arguments.</param>
static void MainSafe(string[] args)
{
// Check command line arguments
if (args.Length < 1) throw new Exception("File name missing!");
if (args.Length < 2) throw new Exception("Language!");
// Pickup filename and language
string file = Path.GetFullPath(args[0]);
string lang = args[1];
// Detect file type
if (file.EndsWith(".xlsx", StringComparison.OrdinalIgnoreCase))
{
TranslateExcel(file, lang);
}
else if (file.EndsWith(".pptx", StringComparison.OrdinalIgnoreCase))
{
TranslatePowerPoint(file, lang);
}
}
/// <summary>
/// Translates the Excel workbook.
/// </summary>
static void TranslateExcel(string file, string lang)
{
// Translate text
string txt = null;
// Google translation client
var gtc = TranslationClient.Create();
// Launch Excel
var exa = new Excel.Application();
// Open workbook
Workbook wb = exa.Workbooks.Open(file, ReadOnly: true);
// Iterate all worksheets
foreach (Excel.Worksheet ws in wb.Worksheets)
{
// Action counter
int i = 0;
// Onlide slide worksheets
if (ws.Name.StartsWith("Slide", StringComparison.OrdinalIgnoreCase))
{
while (ws.Range["B2"].Offset[i, 0].Text.ToString() != "")
{
// Get argument
string arg = ws.Range["B2"].Offset[i, 0].Text.ToString();
// Check type
bool ctrl = ("#@~".Contains(arg[0]));
// Only translate non-control arguments
if (!ctrl)
{
// Make the API call
txt = gtc.TranslateText(arg, lang, "en").TranslatedText;
// Update the workbook
ws.Range["B2"].Offset[i, 0].Value = txt;
// Show progress
Console.WriteLine(arg);
Console.WriteLine(txt);
Console.WriteLine();
}
// Increment action counter
i++;
}
}
}
// Update the file with the language code
file = Path.Combine(Path.GetDirectoryName(file), Path.GetFileNameWithoutExtension(file) + "." + lang + ".xlsx");
// Save the workbook
wb.SaveAs(file);
// Progress
Console.WriteLine("Output: " + file);
Console.WriteLine();
Console.WriteLine("Ready. Please check workbook for translation errors!");
// Quit Excel
try { wb.Close(); } catch { }
try { exa.Quit(); } catch { }
try { Kill("EXCEL", ""); } catch { }
}
/// <summary>
/// Translates the Excel workbook.
/// </summary>
static void TranslatePowerPoint(string file, string lang)
{
//string[] fonts = { "Consolas", "Courier New", "Lucida Sans Typewriter" };
// Translate text
string txt = null;
// Google translation client
var gtc = TranslationClient.Create();
// Launch powerpoint
var ppa = new PowerPoint.Application();
// Open presentation
var ppp = ppa.Presentations.Open(file);
// Move the focus to the console window
FocusWindow(Console.Title);
// Iterate all slides
foreach (PowerPoint.Slide slide in ppp.Slides)
{
foreach (PowerPoint.Shape shape in slide.Shapes)
{
if (shape.HasTextFrame == MsoTriState.msoTrue)
{
// Analyze font
string fname = shape.TextFrame.TextRange.Font.Name;
float fsize = shape.TextFrame.TextRange.Font.Size;
var font = new System.Drawing.Font(fname, fsize);
var monospace = IsMonospacedFont(font);
var equation = fname.Contains("Math", StringComparison.OrdinalIgnoreCase);
var exclude = (shape.AlternativeText == "@");
// We assume monospaced fonts represent code that does not require translation
// We also do not want to touch equations because all formats will be lost
// Set the shape Alt Text to "@" to exclude the shape from translation
if (!monospace && !equation && !exclude)
{
// Preserve layout by sizing the text to the shape
shape.TextFrame2.AutoSize = MsoAutoSize.msoAutoSizeTextToFitShape;
// Get the text
string arg = shape.TextFrame2.TextRange.Text.Trim();
// Check for empty strings
if (!string.IsNullOrEmpty(arg))
{
// Make the API call
txt = gtc.TranslateText(arg, lang, "en").TranslatedText;
// Update the presentation
shape.TextFrame2.TextRange.Text = txt; // This destroys all formatting
//shape.TextFrame.TextRange.Font.Color.RGB = Color.Black.ToArgb(); // Reset color to black
// Show progress
Console.WriteLine(arg);
Console.WriteLine(txt);
Console.WriteLine();
}
}
}
}
}
// Update the file with the language code
file = Path.Combine(Path.GetDirectoryName(file), Path.GetFileNameWithoutExtension(file) + "." + lang + ".pptx");
// Save the workbook
ppp.SaveAs(file);
// Progress
Console.WriteLine("Output: " + file);
Console.WriteLine();
Console.WriteLine("Ready. Please check presentation for colors, layout and translation errors!");
// Quit PowerPoint
try { ppp.Close(); } catch { }
try { ppa.Quit(); } catch { }
try { Kill("POWERPNT", "PowerPoint"); } catch { }
}
public static bool IsMonospacedFont(System.Drawing.Font font)
{
using (Bitmap bitmap = new Bitmap(1, 1))
{
using (Graphics g = Graphics.FromImage(bitmap))
{
// Choose two characters with different widths, e.g., 'i' and 'W'
string testString = "iW";
SizeF size1 = g.MeasureString(testString[0].ToString(), font);
SizeF size2 = g.MeasureString(testString[1].ToString(), font);
// If the widths are different, it's not a monospaced font
return size1.Width == size2.Width;
}
}
}
/// <summary>Moves the focus to window.</summary>
static void FocusWindow(string title)
{
FocusWindow(FindWindowByCaption(0, Console.Title));
}
/// <summary>Moves the focus to window.</summary>
static void FocusWindow(IntPtr hwnd)
{
SetForegroundWindow(hwnd);
}
/// <summary>
/// Kills the process indentified by its signature.
/// </summary>
/// <param name="signature"></param>
static void Kill(string signature, string title)
{
// Create an array of all running Excel processes
Process[] processes = Process.GetProcessesByName(signature);
// Loop over these processes
foreach (var process in processes)
{
// Only look at the instance with an empty window title
if (process.MainWindowTitle == title)
{
// Kill the process
process.Kill();
}
}
}
}
}