1 package com.acumenvelocity.ath.filters.pdf;
2
3 import java.io.File;
4 import java.io.FileInputStream;
5 import java.io.FileOutputStream;
6 import java.io.IOException;
7 import java.io.InputStream;
8 import java.io.OutputStream;
9
10 import com.acumenvelocity.ath.common.AthUtil;
11 import com.acumenvelocity.ath.common.Log;
12 import com.acumenvelocity.ath.common.PdfUtil;
13
14 import net.sf.okapi.common.Event;
15 import net.sf.okapi.common.IParameters;
16 import net.sf.okapi.common.LocaleId;
17 import net.sf.okapi.common.StreamUtil;
18 import net.sf.okapi.common.encoder.EncoderManager;
19 import net.sf.okapi.common.exceptions.OkapiIOException;
20 import net.sf.okapi.common.filterwriter.IFilterWriter;
21 import net.sf.okapi.common.skeleton.ISkeletonWriter;
22 import net.sf.okapi.filters.openxml.OpenXMLFilterWriter;
23
24
25
26
27
28 public class AthPdfFilterWriter implements IFilterWriter {
29
30 private OutputStream output;
31 private LocaleId targetLocale;
32 private String defaultEncoding;
33 private IParameters parameters;
34 private boolean isStarted;
35 private boolean isCancelled;
36 private boolean ownsOutputStream;
37
38 private OpenXMLFilterWriter docxWriter;
39 private File tempDocxFile;
40 private OutputStream docxOutputStream;
41
42 public AthPdfFilterWriter(OpenXMLFilterWriter docxWriter) {
43 this.docxWriter = docxWriter;
44 this.isStarted = false;
45 this.isCancelled = false;
46 this.ownsOutputStream = false;
47 }
48
49 @Override
50 public String getName() {
51 return "AthPdfFilterWriter";
52 }
53
54 @Override
55 public void setOptions(LocaleId locale, String defaultEncoding) {
56 this.targetLocale = locale;
57 this.defaultEncoding = defaultEncoding;
58 }
59
60 @Override
61 public void setOutput(String path) {
62 try {
63 File file = new File(path);
64 File parent = file.getParentFile();
65
66 if (parent != null && !parent.exists()) {
67 parent.mkdirs();
68 }
69
70 this.output = new FileOutputStream(file);
71 this.ownsOutputStream = true;
72
73 } catch (IOException e) {
74 throw new OkapiIOException("Error creating output file: " + path, e);
75 }
76 }
77
78 @Override
79 public void setOutput(OutputStream output) {
80 this.output = output;
81 this.ownsOutputStream = false;
82 }
83
84 @Override
85 public Event handleEvent(Event event) {
86 if (isCancelled) {
87 return event;
88 }
89
90 switch (event.getEventType()) {
91 case START_DOCUMENT:
92 handleStartDocument(event);
93 break;
94
95 case END_DOCUMENT:
96 handleEndDocument(event);
97 break;
98
99 case TEXT_UNIT:
100 case DOCUMENT_PART:
101 case START_SUBDOCUMENT:
102 case END_SUBDOCUMENT:
103 case START_GROUP:
104 case END_GROUP:
105 case START_SUBFILTER:
106 case END_SUBFILTER:
107
108 if (docxWriter != null) {
109 docxWriter.handleEvent(event);
110 }
111
112 break;
113
114 default:
115 break;
116 }
117
118 return event;
119 }
120
121 private void handleStartDocument(Event event) {
122 isStarted = true;
123 isCancelled = false;
124
125 Log.info(getClass(), "=== PDF Filter Writer Started ===");
126 Log.info(getClass(), "Target locale: " + targetLocale);
127
128 try {
129
130 tempDocxFile = AthUtil.createTempFile();
131 docxOutputStream = new FileOutputStream(tempDocxFile);
132
133
134
135 if (docxWriter == null) {
136 throw new OkapiIOException("OpenXML filter writer not available");
137 }
138
139 docxWriter.setOptions(targetLocale, defaultEncoding);
140 docxWriter.setOutput(docxOutputStream);
141
142
143 docxWriter.handleEvent(event);
144
145 Log.info(getClass(), "DOCX writer initialized with temp file: " + tempDocxFile.getAbsolutePath());
146
147 } catch (Exception e) {
148 cleanupTempFile();
149 throw new OkapiIOException("Error initializing DOCX writer", e);
150 }
151 }
152
153 private void handleEndDocument(Event event) {
154 if (!isStarted || isCancelled || output == null) {
155 return;
156 }
157
158 InputStream docxInputStream = null;
159
160 try {
161 Log.info(getClass(), "\n=== Writing PDF ===");
162
163
164 if (docxWriter != null) {
165 docxWriter.handleEvent(event);
166 docxWriter.close();
167 }
168
169
170 if (docxOutputStream != null) {
171 docxOutputStream.close();
172 docxOutputStream = null;
173 }
174
175 Log.info(getClass(), "DOCX generation completed. Size: " + tempDocxFile.length() + " bytes");
176
177
178 docxInputStream = new FileInputStream(tempDocxFile);
179
180
181 InputStream pdfInputStream = PdfUtil.convertDocxToPdf(docxInputStream, targetLocale);
182
183
184 StreamUtil.copy(pdfInputStream, output);
185
186 Log.info(getClass(), "PDF written successfully!");
187
188 } catch (Exception e) {
189 Log.error(getClass(), "Error writing PDF", e);
190 throw new OkapiIOException("Error writing translated PDF", e);
191
192 } finally {
193 if (docxInputStream != null) {
194 try {
195 docxInputStream.close();
196
197 } catch (IOException e) {
198
199 }
200 }
201
202 cleanupTempFile();
203 }
204 }
205
206 private void cleanupTempFile() {
207 if (tempDocxFile != null && tempDocxFile.exists()) {
208 try {
209 tempDocxFile.delete();
210 Log.info(getClass(), "Temporary DOCX file deleted");
211
212 } catch (Exception e) {
213 Log.error(getClass(), "Failed to delete temp file: " + tempDocxFile.getAbsolutePath(), e);
214 }
215
216 tempDocxFile = null;
217 }
218 }
219
220 @Override
221 public void close() {
222 Log.info(getClass(), "=== PDF Filter Writer Closed ===\n");
223
224 isStarted = false;
225 isCancelled = false;
226
227 if (docxWriter != null) {
228 try {
229 docxWriter.close();
230
231 } catch (Exception e) {
232 Log.error(getClass(), "Error closing DOCX writer", e);
233 }
234
235 docxWriter = null;
236 }
237
238 if (docxOutputStream != null) {
239 try {
240 docxOutputStream.close();
241
242 } catch (IOException e) {
243
244 }
245
246 docxOutputStream = null;
247 }
248
249 cleanupTempFile();
250
251 if (output != null && ownsOutputStream) {
252 try {
253 output.close();
254
255 } catch (IOException e) {
256 throw new OkapiIOException("Error closing output stream", e);
257 }
258 }
259
260 output = null;
261 }
262
263 @Override
264 public IParameters getParameters() {
265 return parameters;
266 }
267
268 @Override
269 public void setParameters(IParameters params) {
270 this.parameters = params;
271 }
272
273 @Override
274 public void cancel() {
275 isCancelled = true;
276
277 if (docxWriter != null) {
278 docxWriter.cancel();
279 }
280
281 cleanupTempFile();
282 Log.info(getClass(), "PDF Filter Writer cancelled");
283 }
284
285 @Override
286 public EncoderManager getEncoderManager() {
287 return null;
288 }
289
290 @Override
291 public ISkeletonWriter getSkeletonWriter() {
292 return null;
293 }
294
295 public LocaleId getTargetLocale() {
296 return targetLocale;
297 }
298
299 public String getDefaultEncoding() {
300 return defaultEncoding;
301 }
302
303 public boolean isCancelled() {
304 return isCancelled;
305 }
306 }