xref: /haiku/src/add-ons/translators/stxt/STXTTranslator.cpp (revision 02354704729d38c3b078c696adc1bbbd33cbcf72)
1 /*
2  * Copyright 2002-2009, Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Michael Wilber
7  *		Axel Dörfler, axeld@pinc-software.de
8  */
9 
10 
11 #include "STXTTranslator.h"
12 #include "STXTView.h"
13 
14 #include <Catalog.h>
15 #include <CharacterSet.h>
16 #include <CharacterSetRoster.h>
17 #include <MimeType.h>
18 #include <String.h>
19 #include <TextEncoding.h>
20 #include <UTF8.h>
21 
22 #include <algorithm>
23 #include <new>
24 #include <string.h>
25 #include <stdio.h>
26 #include <stdint.h>
27 
28 
29 using namespace BPrivate;
30 using namespace std;
31 
32 #undef B_TRANSLATION_CONTEXT
33 #define B_TRANSLATION_CONTEXT "STXTTranslator"
34 
35 #define READ_BUFFER_SIZE 32768
36 #define DATA_BUFFER_SIZE 8192
37 
38 // The input formats that this translator supports.
39 static const translation_format sInputFormats[] = {
40 	{
41 		B_TRANSLATOR_TEXT,
42 		B_TRANSLATOR_TEXT,
43 		TEXT_IN_QUALITY,
44 		TEXT_IN_CAPABILITY,
45 		"text/plain",
46 		"Plain text file"
47 	},
48 	{
49 		B_STYLED_TEXT_FORMAT,
50 		B_TRANSLATOR_TEXT,
51 		STXT_IN_QUALITY,
52 		STXT_IN_CAPABILITY,
53 		"text/x-vnd.Be-stxt",
54 		"Be styled text file"
55 	}
56 };
57 
58 // The output formats that this translator supports.
59 static const translation_format sOutputFormats[] = {
60 	{
61 		B_TRANSLATOR_TEXT,
62 		B_TRANSLATOR_TEXT,
63 		TEXT_OUT_QUALITY,
64 		TEXT_OUT_CAPABILITY,
65 		"text/plain",
66 		"Plain text file"
67 	},
68 	{
69 		B_STYLED_TEXT_FORMAT,
70 		B_TRANSLATOR_TEXT,
71 		STXT_OUT_QUALITY,
72 		STXT_OUT_CAPABILITY,
73 		"text/x-vnd.Be-stxt",
74 		"Be styled text file"
75 	}
76 };
77 
78 // Default settings for the Translator
79 static const TranSetting sDefaultSettings[] = {
80 	{B_TRANSLATOR_EXT_HEADER_ONLY, TRAN_SETTING_BOOL, false},
81 	{B_TRANSLATOR_EXT_DATA_ONLY, TRAN_SETTING_BOOL, false}
82 };
83 
84 const uint32 kNumInputFormats = sizeof(sInputFormats) / sizeof(translation_format);
85 const uint32 kNumOutputFormats = sizeof(sOutputFormats) / sizeof(translation_format);
86 const uint32 kNumDefaultSettings = sizeof(sDefaultSettings) / sizeof(TranSetting);
87 
88 // ---------------------------------------------------------------
89 // make_nth_translator
90 //
91 // Creates a STXTTranslator object to be used by BTranslatorRoster
92 //
93 // Preconditions:
94 //
95 // Parameters: n,		The translator to return. Since
96 //						STXTTranslator only publishes one
97 //						translator, it only returns a
98 //						STXTTranslator if n == 0
99 //
100 //             you, 	The image_id of the add-on that
101 //						contains code (not used).
102 //
103 //             flags,	Has no meaning yet, should be 0.
104 //
105 // Postconditions:
106 //
107 // Returns: NULL if n is not zero,
108 //          a new STXTTranslator if n is zero
109 // ---------------------------------------------------------------
110 BTranslator *
111 make_nth_translator(int32 n, image_id you, uint32 flags, ...)
112 {
113 	if (!n)
114 		return new (std::nothrow) STXTTranslator();
115 
116 	return NULL;
117 }
118 
119 
120 //	#pragma mark -
121 
122 
123 /*!
124 	Determines if the data in inSource is of the STXT format.
125 
126 	\param header the STXT stream header read in by Identify() or Translate()
127 	\param inSource the stream with the STXT data
128 	\param outInfo information about the type of data from inSource is stored here
129 	\param outType the desired output type for the data in inSource
130 	\param ptxtheader if this is not NULL, the TEXT header from
131 		inSource is copied to it
132 */
133 status_t
134 identify_stxt_header(const TranslatorStyledTextStreamHeader &header,
135 	BPositionIO *inSource, translator_info *outInfo, uint32 outType,
136 	TranslatorStyledTextTextHeader *ptxtheader = NULL)
137 {
138 	const ssize_t ktxtsize = sizeof(TranslatorStyledTextTextHeader);
139 	const ssize_t kstylsize = sizeof(TranslatorStyledTextStyleHeader);
140 
141 	uint8 buffer[max(ktxtsize, kstylsize)];
142 
143 	// Check the TEXT header
144 	TranslatorStyledTextTextHeader txtheader;
145 	if (inSource->Read(buffer, ktxtsize) != ktxtsize)
146 		return B_NO_TRANSLATOR;
147 
148 	memcpy(&txtheader, buffer, ktxtsize);
149 	if (swap_data(B_UINT32_TYPE, &txtheader, ktxtsize,
150 		B_SWAP_BENDIAN_TO_HOST) != B_OK)
151 		return B_ERROR;
152 
153 	if (txtheader.header.magic != 'TEXT'
154 		|| txtheader.header.header_size != sizeof(TranslatorStyledTextTextHeader)
155 		|| txtheader.charset != B_UNICODE_UTF8)
156 		return B_NO_TRANSLATOR;
157 
158 	// skip the text data
159 	off_t seekresult, pos;
160 	pos = header.header.header_size + txtheader.header.header_size
161 		+ txtheader.header.data_size;
162 	seekresult = inSource->Seek(txtheader.header.data_size,
163 		SEEK_CUR);
164 	if (seekresult < pos)
165 		return B_NO_TRANSLATOR;
166 	if (seekresult > pos)
167 		return B_ERROR;
168 
169 	// check the STYL header (not all STXT files have this)
170 	ssize_t read = 0;
171 	TranslatorStyledTextStyleHeader stylheader;
172 	read = inSource->Read(buffer, kstylsize);
173 	if (read < 0)
174 		return read;
175 	if (read != kstylsize && read != 0)
176 		return B_NO_TRANSLATOR;
177 
178 	// If there is a STYL header
179 	if (read == kstylsize) {
180 		memcpy(&stylheader, buffer, kstylsize);
181 		if (swap_data(B_UINT32_TYPE, &stylheader, kstylsize,
182 			B_SWAP_BENDIAN_TO_HOST) != B_OK)
183 			return B_ERROR;
184 
185 		if (stylheader.header.magic != 'STYL'
186 			|| stylheader.header.header_size !=
187 				sizeof(TranslatorStyledTextStyleHeader))
188 			return B_NO_TRANSLATOR;
189 	}
190 
191 	// if output TEXT header is supplied, fill it with data
192 	if (ptxtheader) {
193 		ptxtheader->header.magic = txtheader.header.magic;
194 		ptxtheader->header.header_size = txtheader.header.header_size;
195 		ptxtheader->header.data_size = txtheader.header.data_size;
196 		ptxtheader->charset = txtheader.charset;
197 	}
198 
199 	// return information about the data in the stream
200 	outInfo->type = B_STYLED_TEXT_FORMAT;
201 	outInfo->group = B_TRANSLATOR_TEXT;
202 	outInfo->quality = STXT_IN_QUALITY;
203 	outInfo->capability = STXT_IN_CAPABILITY;
204 	strlcpy(outInfo->name, B_TRANSLATE("Be styled text file"),
205 		sizeof(outInfo->name));
206 	strcpy(outInfo->MIME, "text/x-vnd.Be-stxt");
207 
208 	return B_OK;
209 }
210 
211 
212 /*!
213 	Determines if the data in \a inSource is of the UTF8 plain
214 
215 	\param data buffer containing data already read (must be at
216 		least DATA_BUFFER_SIZE bytes large)
217 	\param nread number of bytes that have already been read from the stream
218 	\param header the STXT stream header read in by Identify() or Translate()
219 	\param inSource the stream with the STXT data
220 	\param outInfo information about the type of data from inSource is stored here
221 	\param outType the desired output type for the data in inSource
222 */
223 status_t
224 identify_text(uint8* data, int32 bytesRead, BPositionIO* source,
225 	translator_info* outInfo, uint32 outType, BString& encoding)
226 {
227 	ssize_t readLater = source->Read(data + bytesRead, DATA_BUFFER_SIZE - bytesRead);
228 	if (readLater < B_OK)
229 		return B_NO_TRANSLATOR;
230 
231 	bytesRead += readLater;
232 
233 	BPrivate::BTextEncoding textEncoding((char*)data, (size_t)bytesRead);
234 	encoding = textEncoding.GetName();
235 	if (encoding.IsEmpty()) {
236 		/* No valid character encoding found! */
237 		return B_NO_TRANSLATOR;
238 	}
239 
240 	float capability = TEXT_IN_CAPABILITY;
241 	if (bytesRead < 20)
242 		capability = .1f;
243 
244 	// return information about the data in the stream
245 	outInfo->type = B_TRANSLATOR_TEXT;
246 	outInfo->group = B_TRANSLATOR_TEXT;
247 	outInfo->quality = TEXT_IN_QUALITY;
248 	outInfo->capability = capability;
249 
250 	strlcpy(outInfo->name, B_TRANSLATE("Plain text file"),
251 		sizeof(outInfo->name));
252 
253 	//strlcpy(outInfo->MIME, type.Type(), sizeof(outInfo->MIME));
254 	strcpy(outInfo->MIME, "text/plain");
255 	return B_OK;
256 }
257 
258 
259 // ---------------------------------------------------------------
260 // translate_from_stxt
261 //
262 // Translates the data in inSource to the type outType and stores
263 // the translated data in outDestination.
264 //
265 // Preconditions:
266 //
267 // Parameters:	inSource,	the data to be translated
268 //
269 //				outDestination,	where the translated data is
270 //								put
271 //
272 //				outType,	the type to convert inSource to
273 //
274 //				txtheader, 	the TEXT header from inSource
275 //
276 //
277 // Postconditions:
278 //
279 // Returns: B_BAD_VALUE, if outType is invalid
280 //
281 // B_NO_TRANSLATOR, if this translator doesn't understand the data
282 //
283 // B_ERROR, if there was an error allocating memory or converting
284 //          data
285 //
286 // B_OK, if all went well
287 // ---------------------------------------------------------------
288 status_t
289 translate_from_stxt(BPositionIO *inSource, BPositionIO *outDestination,
290 		uint32 outType, const TranslatorStyledTextTextHeader &txtheader)
291 {
292 	if (inSource->Seek(0, SEEK_SET) != 0)
293 		return B_ERROR;
294 
295 	const ssize_t kstxtsize = sizeof(TranslatorStyledTextStreamHeader);
296 	const ssize_t ktxtsize = sizeof(TranslatorStyledTextTextHeader);
297 
298 	bool btoplain;
299 	if (outType == B_TRANSLATOR_TEXT)
300 		btoplain = true;
301 	else if (outType == B_STYLED_TEXT_FORMAT)
302 		btoplain = false;
303 	else
304 		return B_BAD_VALUE;
305 
306 	uint8 buffer[READ_BUFFER_SIZE];
307 	ssize_t nread = 0, nwritten = 0, nreed = 0, ntotalread = 0;
308 
309 	// skip to the actual text data when outputting a
310 	// plain text file
311 	if (btoplain) {
312 		if (inSource->Seek(kstxtsize + ktxtsize, SEEK_CUR) !=
313 			kstxtsize + ktxtsize)
314 			return B_ERROR;
315 	}
316 
317 	// Read data from inSource
318 	// When outputing B_TRANSLATOR_TEXT, the loop stops when all of
319 	// the text data has been read and written.
320 	// When outputting B_STYLED_TEXT_FORMAT, the loop stops when all
321 	// of the data from inSource has been read and written.
322 	if (btoplain)
323 		nreed = min((size_t)READ_BUFFER_SIZE,
324 			(size_t)txtheader.header.data_size - ntotalread);
325 	else
326 		nreed = READ_BUFFER_SIZE;
327 	nread = inSource->Read(buffer, nreed);
328 	while (nread > 0) {
329 		nwritten = outDestination->Write(buffer, nread);
330 		if (nwritten != nread)
331 			return B_ERROR;
332 
333 		if (btoplain) {
334 			ntotalread += nread;
335 			nreed = min((size_t)READ_BUFFER_SIZE,
336 				(size_t)txtheader.header.data_size - ntotalread);
337 		} else
338 			nreed = READ_BUFFER_SIZE;
339 		nread = inSource->Read(buffer, nreed);
340 	}
341 
342 	if (btoplain && static_cast<ssize_t>(txtheader.header.data_size) !=
343 		ntotalread)
344 		// If not all of the text data was able to be read...
345 		return B_NO_TRANSLATOR;
346 	else
347 		return B_OK;
348 }
349 
350 // ---------------------------------------------------------------
351 // output_headers
352 //
353 // Outputs the Stream and Text headers from the B_STYLED_TEXT_FORMAT
354 // to outDestination, setting the data_size member of the text header
355 // to text_data_size
356 //
357 // Preconditions:
358 //
359 // Parameters:	outDestination,	where the translated data is
360 //								put
361 //
362 //				text_data_size, number of bytes in data section
363 //							    of the TEXT header
364 //
365 //
366 // Postconditions:
367 //
368 // Returns:
369 //
370 // B_ERROR, if there was an error writing to outDestination or
371 // 	an error with converting the byte order
372 //
373 // B_OK, if all went well
374 // ---------------------------------------------------------------
375 status_t
376 output_headers(BPositionIO *outDestination, uint32 text_data_size)
377 {
378 	const int32 kHeadersSize = sizeof(TranslatorStyledTextStreamHeader) +
379 		sizeof(TranslatorStyledTextTextHeader);
380 	status_t result;
381 	TranslatorStyledTextStreamHeader stxtheader;
382 	TranslatorStyledTextTextHeader txtheader;
383 
384 	uint8 buffer[kHeadersSize];
385 
386 	stxtheader.header.magic = 'STXT';
387 	stxtheader.header.header_size = sizeof(TranslatorStyledTextStreamHeader);
388 	stxtheader.header.data_size = 0;
389 	stxtheader.version = 100;
390 	memcpy(buffer, &stxtheader, stxtheader.header.header_size);
391 
392 	txtheader.header.magic = 'TEXT';
393 	txtheader.header.header_size = sizeof(TranslatorStyledTextTextHeader);
394 	txtheader.header.data_size = text_data_size;
395 	txtheader.charset = B_UNICODE_UTF8;
396 	memcpy(buffer + stxtheader.header.header_size, &txtheader,
397 		txtheader.header.header_size);
398 
399 	// write out headers in Big Endian byte order
400 	result = swap_data(B_UINT32_TYPE, buffer, kHeadersSize,
401 		B_SWAP_HOST_TO_BENDIAN);
402 	if (result == B_OK) {
403 		ssize_t nwritten = 0;
404 		nwritten = outDestination->Write(buffer, kHeadersSize);
405 		if (nwritten != kHeadersSize)
406 			return B_ERROR;
407 		else
408 			return B_OK;
409 	}
410 
411 	return result;
412 }
413 
414 // ---------------------------------------------------------------
415 // output_styles
416 //
417 // Writes out the actual style information into outDestination
418 // using the data from pflatRunArray
419 //
420 // Preconditions:
421 //
422 // Parameters:	outDestination,	where the translated data is
423 //								put
424 //
425 //				text_size,		size in bytes of the text in
426 //								outDestination
427 //
428 //				data_size,		size of pflatRunArray
429 //
430 // Postconditions:
431 //
432 // Returns:
433 //
434 // B_ERROR, if there was an error writing to outDestination or
435 // 	an error with converting the byte order
436 //
437 // B_OK, if all went well
438 // ---------------------------------------------------------------
439 status_t
440 output_styles(BPositionIO *outDestination, uint32 text_size,
441 	uint8 *pflatRunArray, ssize_t data_size)
442 {
443 	const ssize_t kstylsize = sizeof(TranslatorStyledTextStyleHeader);
444 
445 	uint8 buffer[kstylsize];
446 
447 	// output STYL header
448 	TranslatorStyledTextStyleHeader stylheader;
449 	stylheader.header.magic = 'STYL';
450 	stylheader.header.header_size =
451 		sizeof(TranslatorStyledTextStyleHeader);
452 	stylheader.header.data_size = data_size;
453 	stylheader.apply_offset = 0;
454 	stylheader.apply_length = text_size;
455 
456 	memcpy(buffer, &stylheader, kstylsize);
457 	if (swap_data(B_UINT32_TYPE, buffer, kstylsize,
458 		B_SWAP_HOST_TO_BENDIAN) != B_OK)
459 		return B_ERROR;
460 	if (outDestination->Write(buffer, kstylsize) != kstylsize)
461 		return B_ERROR;
462 
463 	// output actual style information
464 	if (outDestination->Write(pflatRunArray,
465 		data_size) != data_size)
466 		return B_ERROR;
467 
468 	return B_OK;
469 }
470 
471 
472 /*!
473 	Convert the plain text (UTF8) from inSource to plain or
474 	styled text in outDestination
475 */
476 status_t
477 translate_from_text(BPositionIO* source, BString encoding, bool forceEncoding,
478 	BPositionIO* destination, uint32 outType)
479 {
480 	if (outType != B_TRANSLATOR_TEXT && outType != B_STYLED_TEXT_FORMAT)
481 		return B_BAD_VALUE;
482 
483 	// find the length of the text
484 	off_t size = source->Seek(0, SEEK_END);
485 	if (size < 0)
486 		return (status_t)size;
487 	if (size > UINT32_MAX && outType == B_STYLED_TEXT_FORMAT)
488 		return B_NOT_SUPPORTED;
489 
490 	status_t status = source->Seek(0, SEEK_SET);
491 	if (status < B_OK)
492 		return status;
493 
494 	if (outType == B_STYLED_TEXT_FORMAT) {
495 		// output styled text headers
496 		status = output_headers(destination, (uint32)size);
497 		if (status != B_OK)
498 			return status;
499 	}
500 
501 	class MallocBuffer {
502 		public:
503 			MallocBuffer() : fBuffer(NULL), fSize(0) {}
504 			~MallocBuffer() { free(fBuffer); }
505 
506 			void* Buffer() { return fBuffer; }
507 			size_t Size() const { return fSize; }
508 
509 			status_t
510 			Allocate(size_t size)
511 			{
512 				fBuffer = malloc(size);
513 				if (fBuffer != NULL) {
514 					fSize = size;
515 					return B_OK;
516 				}
517 				return B_NO_MEMORY;
518 			}
519 
520 		private:
521 			void*	fBuffer;
522 			size_t	fSize;
523 	} encodingBuffer;
524 
525 	BNode* node = dynamic_cast<BNode*>(source);
526 	if (node != NULL) {
527 		// determine encoding, if available
528 		bool hasAttribute = false;
529 		if (encoding.String() && !forceEncoding) {
530 			attr_info info;
531 			node->GetAttrInfo("be:encoding", &info);
532 
533 			if ((info.type == B_STRING_TYPE) && (node->ReadAttrString(
534 					"be:encoding", &encoding) == B_OK)) {
535 				hasAttribute = true;
536 			} else if (info.type == B_INT32_TYPE) {
537 				// Try the BeOS version of the atribute, which used an int32
538 				// and a well-known list of encodings.
539 				int32 value;
540 				ssize_t bytesRead = node->ReadAttr("be:encoding", B_INT32_TYPE, 0,
541 					&value, sizeof(value));
542 				if (bytesRead == (ssize_t)sizeof(value)) {
543 					if (value != 65535) {
544 						const BCharacterSet* characterSet
545 							= BCharacterSetRoster::GetCharacterSetByConversionID(value);
546 						if (characterSet != NULL)
547 							encoding = characterSet->GetName();
548 					}
549 				}
550 			}
551 		} else {
552 			hasAttribute = true;
553 				// we don't write the encoding in this case
554 		}
555 
556 		if (!encoding.IsEmpty())
557 			encodingBuffer.Allocate(READ_BUFFER_SIZE * 4);
558 
559 		if (!hasAttribute && !encoding.IsEmpty()) {
560 			// add encoding attribute, so that someone opening the file can
561 			// retrieve it for persistance
562 			node->WriteAttrString("be:encoding", &encoding);
563 		}
564 	}
565 
566 	off_t outputSize = 0;
567 	ssize_t bytesRead;
568 
569 	BPrivate::BTextEncoding codec(encoding.String());
570 
571 	// output the actual text part of the data
572 	do {
573 		uint8 buffer[READ_BUFFER_SIZE];
574 		bytesRead = source->Read(buffer, READ_BUFFER_SIZE);
575 		if (bytesRead < B_OK)
576 			return bytesRead;
577 		if (bytesRead == 0)
578 			break;
579 
580 		if (encodingBuffer.Size() == 0) {
581 			// default, no encoding
582 			ssize_t bytesWritten = destination->Write(buffer, bytesRead);
583 			if (bytesWritten != bytesRead) {
584 				if (bytesWritten < B_OK)
585 					return bytesWritten;
586 
587 				return B_ERROR;
588 			}
589 
590 			outputSize += bytesRead;
591 		} else {
592 			// decode text file to UTF-8
593 			const char* pos = (char*)buffer;
594 			size_t encodingLength;
595 			int32 bytesLeft = bytesRead;
596 			size_t bytes;
597 			do {
598 				encodingLength = READ_BUFFER_SIZE * 4;
599 				bytes = bytesLeft;
600 
601 				status = codec.Decode(pos, bytes,
602 					(char*)encodingBuffer.Buffer(), encodingLength);
603 				if (status < B_OK) {
604 					return status;
605 				}
606 
607 				ssize_t bytesWritten = destination->Write(encodingBuffer.Buffer(),
608 					encodingLength);
609 				if (bytesWritten < (ssize_t)encodingLength) {
610 					if (bytesWritten < B_OK)
611 						return bytesWritten;
612 
613 					return B_ERROR;
614 				}
615 
616 				pos += bytes;
617 				bytesLeft -= bytes;
618 				outputSize += encodingLength;
619 			} while (encodingLength > 0 && bytesLeft > 0);
620 		}
621 	} while (bytesRead > 0);
622 
623 	if (outType != B_STYLED_TEXT_FORMAT)
624 		return B_OK;
625 
626 	if (encodingBuffer.Size() != 0 && size != outputSize) {
627 		if (outputSize > UINT32_MAX)
628 			return B_NOT_SUPPORTED;
629 
630 		// we need to update the header as the decoded text size has changed
631 		status = destination->Seek(0, SEEK_SET);
632 		if (status == B_OK)
633 			status = output_headers(destination, (uint32)outputSize);
634 		if (status == B_OK)
635 			status = destination->Seek(0, SEEK_END);
636 
637 		if (status < B_OK)
638 			return status;
639 	}
640 
641 	// Read file attributes if outputting styled data
642 	// and source is a BNode object
643 
644 	if (node == NULL)
645 		return B_OK;
646 
647 	// Try to read styles - we only propagate an error if the actual on-disk
648 	// data is likely to be okay
649 
650 	const char *kAttrName = "styles";
651 	attr_info info;
652 	if (node->GetAttrInfo(kAttrName, &info) != B_OK)
653 		return B_OK;
654 
655 	if (info.type != B_RAW_TYPE || info.size < 160) {
656 		// styles seem to be broken, but since we got the text,
657 		// we don't propagate the error
658 		return B_OK;
659 	}
660 
661 	uint8* flatRunArray = new (std::nothrow) uint8[info.size];
662 	if (flatRunArray == NULL)
663 		return B_NO_MEMORY;
664 
665 	bytesRead = node->ReadAttr(kAttrName, B_RAW_TYPE, 0, flatRunArray, info.size);
666 	if (bytesRead != info.size)
667 		return B_OK;
668 
669 	output_styles(destination, size, flatRunArray, info.size);
670 
671 	delete[] flatRunArray;
672 	return B_OK;
673 }
674 
675 
676 //	#pragma mark -
677 
678 
679 STXTTranslator::STXTTranslator()
680 	: BaseTranslator(B_TRANSLATE("StyledEdit files"),
681 		B_TRANSLATE("StyledEdit file translator"),
682 		STXT_TRANSLATOR_VERSION,
683 		sInputFormats, kNumInputFormats,
684 		sOutputFormats, kNumOutputFormats,
685 		"STXTTranslator_Settings",
686 		sDefaultSettings, kNumDefaultSettings,
687 		B_TRANSLATOR_TEXT, B_STYLED_TEXT_FORMAT)
688 {
689 }
690 
691 
692 STXTTranslator::~STXTTranslator()
693 {
694 }
695 
696 
697 status_t
698 STXTTranslator::Identify(BPositionIO *inSource,
699 	const translation_format *inFormat, BMessage *ioExtension,
700 	translator_info *outInfo, uint32 outType)
701 {
702 	if (!outType)
703 		outType = B_TRANSLATOR_TEXT;
704 	if (outType != B_TRANSLATOR_TEXT && outType != B_STYLED_TEXT_FORMAT)
705 		return B_NO_TRANSLATOR;
706 
707 	const ssize_t kstxtsize = sizeof(TranslatorStyledTextStreamHeader);
708 
709 	uint8 buffer[DATA_BUFFER_SIZE];
710 	status_t nread = 0;
711 	// Read in the header to determine
712 	// if the data is supported
713 	nread = inSource->Read(buffer, kstxtsize);
714 	if (nread < 0)
715 		return nread;
716 
717 	// read in enough data to fill the stream header
718 	if (nread == kstxtsize) {
719 		TranslatorStyledTextStreamHeader header;
720 		memcpy(&header, buffer, kstxtsize);
721 		if (swap_data(B_UINT32_TYPE, &header, kstxtsize,
722 				B_SWAP_BENDIAN_TO_HOST) != B_OK)
723 			return B_ERROR;
724 
725 		if (header.header.magic == B_STYLED_TEXT_FORMAT
726 			&& header.header.header_size == (int32)kstxtsize
727 			&& header.header.data_size == 0
728 			&& header.version == 100)
729 			return identify_stxt_header(header, inSource, outInfo, outType);
730 	}
731 
732 	// if the data is not styled text, check if it is plain text
733 	BString encoding;
734 	return identify_text(buffer, nread, inSource, outInfo, outType, encoding);
735 }
736 
737 
738 status_t
739 STXTTranslator::Translate(BPositionIO* source, const translator_info* info,
740 	BMessage* ioExtension, uint32 outType, BPositionIO* outDestination)
741 {
742 	if (!outType)
743 		outType = B_TRANSLATOR_TEXT;
744 	if (outType != B_TRANSLATOR_TEXT && outType != B_STYLED_TEXT_FORMAT)
745 		return B_NO_TRANSLATOR;
746 
747 	const ssize_t headerSize = sizeof(TranslatorStyledTextStreamHeader);
748 	uint8 buffer[DATA_BUFFER_SIZE];
749 	status_t result;
750 	translator_info outInfo;
751 	// Read in the header to determine
752 	// if the data is supported
753 	ssize_t bytesRead = source->Read(buffer, headerSize);
754 	if (bytesRead < 0)
755 		return bytesRead;
756 
757 	// read in enough data to fill the stream header
758 	if (bytesRead == headerSize) {
759 		TranslatorStyledTextStreamHeader header;
760 		memcpy(&header, buffer, headerSize);
761 		if (swap_data(B_UINT32_TYPE, &header, headerSize,
762 				B_SWAP_BENDIAN_TO_HOST) != B_OK)
763 			return B_ERROR;
764 
765 		if (header.header.magic == B_STYLED_TEXT_FORMAT
766 			&& header.header.header_size == sizeof(TranslatorStyledTextStreamHeader)
767 			&& header.header.data_size == 0
768 			&& header.version == 100) {
769 			TranslatorStyledTextTextHeader textHeader;
770 			result = identify_stxt_header(header, source, &outInfo, outType,
771 				&textHeader);
772 			if (result != B_OK)
773 				return result;
774 
775 			return translate_from_stxt(source, outDestination, outType, textHeader);
776 		}
777 	}
778 
779 	// if the data is not styled text, check if it is ASCII text
780 	bool forceEncoding = false;
781 	BString encoding;
782 	result = identify_text(buffer, bytesRead, source, &outInfo, outType, encoding);
783 	if (result != B_OK)
784 		return result;
785 
786 	if (ioExtension != NULL) {
787 		const char* value;
788 		if (ioExtension->FindString("be:encoding", &value) == B_OK
789 			&& value[0]) {
790 			// override encoding
791 			encoding = value;
792 			forceEncoding = true;
793 		}
794 	}
795 
796 	return translate_from_text(source, encoding, forceEncoding, outDestination, outType);
797 }
798 
799 
800 BView *
801 STXTTranslator::NewConfigView(TranslatorSettings *settings)
802 {
803 	return new STXTView(BRect(0, 0, 225, 175),
804 		B_TRANSLATE("STXTTranslator Settings"),
805 		B_FOLLOW_ALL, B_WILL_DRAW, settings);
806 }
807 
808