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
29using namespace BPrivate;
30using 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.
39static 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.
59static 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
79static 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
84const uint32 kNumInputFormats = sizeof(sInputFormats) / sizeof(translation_format);
85const uint32 kNumOutputFormats = sizeof(sOutputFormats) / sizeof(translation_format);
86const 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// ---------------------------------------------------------------
110BTranslator *
111make_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*/
133status_t
134identify_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*/
223status_t
224identify_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// ---------------------------------------------------------------
288status_t
289translate_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// ---------------------------------------------------------------
375status_t
376output_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// ---------------------------------------------------------------
439status_t
440output_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*/
476status_t
477translate_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
679STXTTranslator::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
692STXTTranslator::~STXTTranslator()
693{
694}
695
696
697status_t
698STXTTranslator::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
738status_t
739STXTTranslator::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
800BView *
801STXTTranslator::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