xref: /haiku/src/add-ons/translators/stxt/STXTTranslator.cpp (revision df4074fbed092b09474695aa286752ca41625332)
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 2048
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, const char*& 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 (strlen(encoding) == 0) {
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, const char* outEncoding, 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 	BMallocIO encodingIO;
525 
526 	BNode* node = dynamic_cast<BNode*>(source);
527 	BString encoding(outEncoding);
528 	if (node != NULL) {
529 		// determine encoding, if available
530 		bool hasAttribute = false;
531 		if (encoding.String() && !forceEncoding) {
532 			attr_info info;
533 			node->GetAttrInfo("be:encoding", &info);
534 
535 			if ((info.type == B_STRING_TYPE) && (node->ReadAttrString(
536 					"be:encoding", &encoding) == B_OK)) {
537 				hasAttribute = true;
538 			} else if (info.type == B_INT32_TYPE) {
539 				// Try the BeOS version of the atribute, which used an int32
540 				// and a well-known list of encodings.
541 				int32 value;
542 				ssize_t bytesRead = node->ReadAttr("be:encoding", B_INT32_TYPE, 0,
543 					&value, sizeof(value));
544 				if (bytesRead == (ssize_t)sizeof(value)) {
545 					if (value != 65535) {
546 						const BCharacterSet* characterSet
547 							= BCharacterSetRoster::GetCharacterSetByConversionID(value);
548 						if (characterSet != NULL)
549 							encoding = characterSet->GetName();
550 					}
551 				}
552 			}
553 		} else {
554 			hasAttribute = true;
555 				// we don't write the encoding in this case
556 		}
557 
558 		if (!encoding.IsEmpty())
559 			encodingBuffer.Allocate(READ_BUFFER_SIZE * 4);
560 
561 		if (!hasAttribute && !encoding.IsEmpty()) {
562 			// add encoding attribute, so that someone opening the file can
563 			// retrieve it for persistance
564 			node->WriteAttrString("be:encoding", &encoding);
565 		}
566 	}
567 
568 	off_t outputSize = 0;
569 	ssize_t bytesRead;
570 
571 	BPrivate::BTextEncoding codec(encoding.String());
572 
573 	// output the actual text part of the data
574 	do {
575 		uint8 buffer[READ_BUFFER_SIZE];
576 		bytesRead = source->Read(buffer, READ_BUFFER_SIZE);
577 		if (bytesRead < B_OK)
578 			return bytesRead;
579 		if (bytesRead == 0)
580 			break;
581 
582 		if (encodingBuffer.Size() == 0) {
583 			// default, no encoding
584 			ssize_t bytesWritten = destination->Write(buffer, bytesRead);
585 			if (bytesWritten != bytesRead) {
586 				if (bytesWritten < B_OK)
587 					return bytesWritten;
588 
589 				return B_ERROR;
590 			}
591 
592 			outputSize += bytesRead;
593 		} else {
594 			// decode text file to UTF-8
595 			const char* pos = (char*)buffer;
596 			size_t encodingLength = encodingIO.BufferLength();
597 			int32 bytesLeft = bytesRead;
598 			size_t bytes;
599 			do {
600 				encodingLength = READ_BUFFER_SIZE * 4;
601 				bytes = bytesLeft;
602 
603 				status = codec.Decode(pos, bytes,
604 					(char*)encodingBuffer.Buffer(), encodingLength);
605 				if (status < B_OK) {
606 					return status;
607 				}
608 
609 				ssize_t bytesWritten = destination->Write(encodingBuffer.Buffer(),
610 					encodingLength);
611 				if (bytesWritten < (ssize_t)encodingLength) {
612 					if (bytesWritten < B_OK)
613 						return bytesWritten;
614 
615 					return B_ERROR;
616 				}
617 
618 				pos += bytes;
619 				bytesLeft -= bytes;
620 				outputSize += encodingLength;
621 			} while (encodingLength > 0 && bytesLeft > 0);
622 		}
623 	} while (bytesRead > 0);
624 
625 	if (outType != B_STYLED_TEXT_FORMAT)
626 		return B_OK;
627 
628 	if (encodingBuffer.Size() != 0 && size != outputSize) {
629 		if (outputSize > UINT32_MAX)
630 			return B_NOT_SUPPORTED;
631 
632 		// we need to update the header as the decoded text size has changed
633 		status = destination->Seek(0, SEEK_SET);
634 		if (status == B_OK)
635 			status = output_headers(destination, (uint32)outputSize);
636 		if (status == B_OK)
637 			status = destination->Seek(0, SEEK_END);
638 
639 		if (status < B_OK)
640 			return status;
641 	}
642 
643 	// Read file attributes if outputting styled data
644 	// and source is a BNode object
645 
646 	if (node == NULL)
647 		return B_OK;
648 
649 	// Try to read styles - we only propagate an error if the actual on-disk
650 	// data is likely to be okay
651 
652 	const char *kAttrName = "styles";
653 	attr_info info;
654 	if (node->GetAttrInfo(kAttrName, &info) != B_OK)
655 		return B_OK;
656 
657 	if (info.type != B_RAW_TYPE || info.size < 160) {
658 		// styles seem to be broken, but since we got the text,
659 		// we don't propagate the error
660 		return B_OK;
661 	}
662 
663 	uint8* flatRunArray = new (std::nothrow) uint8[info.size];
664 	if (flatRunArray == NULL)
665 		return B_NO_MEMORY;
666 
667 	bytesRead = node->ReadAttr(kAttrName, B_RAW_TYPE, 0, flatRunArray, info.size);
668 	if (bytesRead != info.size)
669 		return B_OK;
670 
671 	output_styles(destination, size, flatRunArray, info.size);
672 
673 	delete[] flatRunArray;
674 	return B_OK;
675 }
676 
677 
678 //	#pragma mark -
679 
680 
681 STXTTranslator::STXTTranslator()
682 	: BaseTranslator(B_TRANSLATE("StyledEdit files"),
683 		B_TRANSLATE("StyledEdit file translator"),
684 		STXT_TRANSLATOR_VERSION,
685 		sInputFormats, kNumInputFormats,
686 		sOutputFormats, kNumOutputFormats,
687 		"STXTTranslator_Settings",
688 		sDefaultSettings, kNumDefaultSettings,
689 		B_TRANSLATOR_TEXT, B_STYLED_TEXT_FORMAT)
690 {
691 }
692 
693 
694 STXTTranslator::~STXTTranslator()
695 {
696 }
697 
698 
699 status_t
700 STXTTranslator::Identify(BPositionIO *inSource,
701 	const translation_format *inFormat, BMessage *ioExtension,
702 	translator_info *outInfo, uint32 outType)
703 {
704 	if (!outType)
705 		outType = B_TRANSLATOR_TEXT;
706 	if (outType != B_TRANSLATOR_TEXT && outType != B_STYLED_TEXT_FORMAT)
707 		return B_NO_TRANSLATOR;
708 
709 	const ssize_t kstxtsize = sizeof(TranslatorStyledTextStreamHeader);
710 
711 	uint8 buffer[DATA_BUFFER_SIZE];
712 	status_t nread = 0;
713 	// Read in the header to determine
714 	// if the data is supported
715 	nread = inSource->Read(buffer, kstxtsize);
716 	if (nread < 0)
717 		return nread;
718 
719 	// read in enough data to fill the stream header
720 	if (nread == kstxtsize) {
721 		TranslatorStyledTextStreamHeader header;
722 		memcpy(&header, buffer, kstxtsize);
723 		if (swap_data(B_UINT32_TYPE, &header, kstxtsize,
724 				B_SWAP_BENDIAN_TO_HOST) != B_OK)
725 			return B_ERROR;
726 
727 		if (header.header.magic == B_STYLED_TEXT_FORMAT
728 			&& header.header.header_size == (int32)kstxtsize
729 			&& header.header.data_size == 0
730 			&& header.version == 100)
731 			return identify_stxt_header(header, inSource, outInfo, outType);
732 	}
733 
734 	// if the data is not styled text, check if it is plain text
735 	const char* encoding;
736 	return identify_text(buffer, nread, inSource, outInfo, outType, encoding);
737 }
738 
739 
740 status_t
741 STXTTranslator::Translate(BPositionIO* source, const translator_info* info,
742 	BMessage* ioExtension, uint32 outType, BPositionIO* outDestination)
743 {
744 	if (!outType)
745 		outType = B_TRANSLATOR_TEXT;
746 	if (outType != B_TRANSLATOR_TEXT && outType != B_STYLED_TEXT_FORMAT)
747 		return B_NO_TRANSLATOR;
748 
749 	const ssize_t headerSize = sizeof(TranslatorStyledTextStreamHeader);
750 	uint8 buffer[DATA_BUFFER_SIZE];
751 	status_t result;
752 	translator_info outInfo;
753 	// Read in the header to determine
754 	// if the data is supported
755 	ssize_t bytesRead = source->Read(buffer, headerSize);
756 	if (bytesRead < 0)
757 		return bytesRead;
758 
759 	// read in enough data to fill the stream header
760 	if (bytesRead == headerSize) {
761 		TranslatorStyledTextStreamHeader header;
762 		memcpy(&header, buffer, headerSize);
763 		if (swap_data(B_UINT32_TYPE, &header, headerSize,
764 				B_SWAP_BENDIAN_TO_HOST) != B_OK)
765 			return B_ERROR;
766 
767 		if (header.header.magic == B_STYLED_TEXT_FORMAT
768 			&& header.header.header_size == sizeof(TranslatorStyledTextStreamHeader)
769 			&& header.header.data_size == 0
770 			&& header.version == 100) {
771 			TranslatorStyledTextTextHeader textHeader;
772 			result = identify_stxt_header(header, source, &outInfo, outType,
773 				&textHeader);
774 			if (result != B_OK)
775 				return result;
776 
777 			return translate_from_stxt(source, outDestination, outType, textHeader);
778 		}
779 	}
780 
781 	// if the data is not styled text, check if it is ASCII text
782 	bool forceEncoding = false;
783 	const char* encoding = NULL;
784 	result = identify_text(buffer, bytesRead, source, &outInfo, outType, encoding);
785 	if (result != B_OK)
786 		return result;
787 
788 	if (ioExtension != NULL) {
789 		const char* value;
790 		if (ioExtension->FindString("be:encoding", &value) == B_OK
791 			&& value[0]) {
792 			// override encoding
793 			encoding = value;
794 			forceEncoding = true;
795 		}
796 	}
797 
798 	return translate_from_text(source, encoding, forceEncoding, outDestination, outType);
799 }
800 
801 
802 BView *
803 STXTTranslator::NewConfigView(TranslatorSettings *settings)
804 {
805 	return new STXTView(BRect(0, 0, 225, 175),
806 		B_TRANSLATE("STXTTranslator Settings"),
807 		B_FOLLOW_ALL, B_WILL_DRAW, settings);
808 }
809 
810