1/*****************************************************************************/
2// stxtinfo
3// Written by Michael Wilber, Haiku Translation Kit Team
4//
5// Version:
6//
7// stxtinfo is a command line program for displaying text information about
8// Be styled text (the format that StyledEdit uses).  StyledEdit stores the
9// styled text information as an attribute of the file and it is this
10// information that this program is concerned with.  This format is outlined
11// in TranslatorFormats.h.
12//
13// This program prints out information from the "styles" attribute that
14// StyledEdit uses.  If that information is not available, it attempts
15// to read the styles information from the contents of the file, assuming
16// that it is the format that BTranslationUtils::PutStyledText() writes
17// out.
18//
19// The intention of this program is to aid with the development and
20// debugging of the STXTTranslator.  It may also be useful for debugging /
21// testing StyledEdit.
22//
23// This application and all source files used in its construction, except
24// where noted, are licensed under the MIT License, and have been written
25// and are:
26//
27// Copyright (c) 2003 Haiku Project
28//
29// Permission is hereby granted, free of charge, to any person obtaining a
30// copy of this software and associated documentation files (the "Software"),
31// to deal in the Software without restriction, including without limitation
32// the rights to use, copy, modify, merge, publish, distribute, sublicense,
33// and/or sell copies of the Software, and to permit persons to whom the
34// Software is furnished to do so, subject to the following conditions:
35//
36// The above copyright notice and this permission notice shall be included
37// in all copies or substantial portions of the Software.
38//
39// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
40// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
41// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
42// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
43// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
44// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
45// DEALINGS IN THE SOFTWARE.
46/*****************************************************************************/
47#include <stdio.h>
48#include <stdlib.h>
49#include <string.h>
50#include <ByteOrder.h>
51#include <File.h>
52#include <TranslatorFormats.h>
53#include <Font.h>
54	// for B_UNICODE_UTF8
55#include <fs_attr.h>
56	// for attr_info
57
58#define max(x,y) ((x > y) ? x : y)
59#define DATA_BUFFER_SIZE 64
60
61struct StylesHeader {
62	uint32 magic;	// 41 6c 69 21
63	uint32 version;	// 0
64	int32 count;
65};
66
67struct Style {
68	int32	offset;
69	char	family[64];
70	char	style[64];
71	float	size;
72	float	shear;	// typically 90.0
73	uint16	face;	// typically 0
74	uint8	red;
75	uint8	green;
76	uint8	blue;
77	uint8	alpha;	// 255 == opaque
78	uint16	reserved; // 0
79};
80
81void
82PrintStyle(Style &style, int32 i)
83{
84	style.offset = B_BENDIAN_TO_HOST_INT32(style.offset);
85	style.size = B_BENDIAN_TO_HOST_FLOAT(style.size);
86	style.shear = B_BENDIAN_TO_HOST_FLOAT(style.shear);
87	style.face = B_BENDIAN_TO_HOST_INT16(style.face);
88	style.reserved = B_BENDIAN_TO_HOST_INT16(style.reserved);
89
90	printf("\nStyle %d:\n", static_cast<int>(i + 1));
91	printf("offset: %d\n", static_cast<int>(style.offset));
92	printf("family: %s\n", style.family);
93	printf("style: %s\n", style.style);
94	printf("size: %f\n", style.size);
95	printf("shear: %f (typically 90.0)\n", style.shear);
96	printf("face: %u (typically 0)\n",
97		static_cast<unsigned int>(style.face));
98	printf("RGBA: (%u, %u, %u, %u)\n",
99		static_cast<unsigned int>(style.red),
100		static_cast<unsigned int>(style.blue),
101		static_cast<unsigned int>(style.green),
102		static_cast<unsigned int>(style.alpha));
103		printf("reserved: %u (should be 0)\n",
104		static_cast<unsigned int>(style.reserved));
105}
106
107bool
108PrintStylesAttribute(BFile &file)
109{
110	const char *kAttrName = "styles";
111	attr_info info;
112
113	if (file.GetAttrInfo(kAttrName, &info) != B_OK)
114		return false;
115	if (info.type != B_RAW_TYPE) {
116		printf("Error: styles attribute is of the wrong type\n");
117		return false;
118	}
119	if (info.size < 160) {
120		printf("Error: styles attribute is missing information\n");
121		return false;
122	}
123
124	uint8 *pflatRunArray = new uint8[info.size];
125	if (!pflatRunArray) {
126		printf("Error: Not enough memory available to read styles attribute\n");
127		return false;
128	}
129
130	ssize_t amtread = file.ReadAttr(kAttrName, B_RAW_TYPE, 0,
131		pflatRunArray, info.size);
132	if (amtread != info.size) {
133		printf("Error: Unable to read styles attribute\n");
134		return false;
135	}
136
137	// Check Styles
138	StylesHeader stylesheader;
139	memcpy(&stylesheader, pflatRunArray, sizeof(StylesHeader));
140	if (swap_data(B_UINT32_TYPE, &stylesheader, sizeof(StylesHeader),
141		B_SWAP_BENDIAN_TO_HOST) != B_OK) {
142		printf("Error: Unable to swap byte order of styles header\n");
143		return false;
144	}
145
146	// Print StylesHeader info
147	printf("\"styles\" attribute data:\n\n");
148
149	printf("magic number: 0x%.8lx ",
150		static_cast<unsigned long>(stylesheader.magic));
151	if (stylesheader.magic == 'Ali!')
152		printf("(valid)\n");
153	else
154		printf("(INVALID, should be 0x%.8lx)\n",
155			static_cast<unsigned long>('Ali!'));
156
157	printf("version: 0x%.8lx ",
158		static_cast<unsigned long>(stylesheader.version));
159	if (stylesheader.version == 0)
160		printf("(valid)\n");
161	else
162		printf("(INVALID, should be 0x%.8lx)\n", 0UL);
163
164	printf("number of styles: %d\n",
165		static_cast<int>(stylesheader.count));
166
167	// Check and Print out each style
168	Style *pstyle = reinterpret_cast<Style *>(pflatRunArray + sizeof(StylesHeader));
169	for (int32 i = 0; i < stylesheader.count; i++)
170		PrintStyle(*(pstyle + i), i);
171
172	delete[] pflatRunArray;
173	pflatRunArray = NULL;
174
175	return true;
176}
177
178bool
179PrintStxtInfo(BFile &file)
180{
181	const uint32 kstxtsize = sizeof(TranslatorStyledTextStreamHeader);
182	const uint32 ktxtsize = sizeof(TranslatorStyledTextTextHeader);
183	const uint32 kstylsize = sizeof(TranslatorStyledTextStyleHeader);
184	const uint32 kStyleSize = sizeof(Style);
185
186	uint8 buffer[max(max(max(kstxtsize, ktxtsize), kstylsize), kStyleSize)];
187
188	// Check STXT Header
189	status_t nread = 0;
190	nread = file.Read(buffer, kstxtsize);
191	if (nread != static_cast<status_t>(kstxtsize)) {
192		printf("Error: Unable to read stream header\n");
193		return false;
194	}
195	TranslatorStyledTextStreamHeader stxtheader;
196	memcpy(&stxtheader, buffer, kstxtsize);
197	if (swap_data(B_UINT32_TYPE, &stxtheader, kstxtsize,
198		B_SWAP_BENDIAN_TO_HOST) != B_OK) {
199		printf("Error: Unable to swap byte order of stream header\n");
200		return false;
201	}
202
203	if (stxtheader.header.magic != B_STYLED_TEXT_FORMAT) {
204		printf("Styled text magic number is incorrect, aborting.\n");
205		return false;
206	}
207
208	// Print Stream Header (STXT)
209	printf("Stream Header (STXT section):\n\n");
210
211	printf("magic number: 0x%.8lx ", stxtheader.header.magic);
212	if (stxtheader.header.magic == B_STYLED_TEXT_FORMAT)
213		printf("(valid)\n");
214	else
215		printf("(INVALID, should be 0x%.8lx)\n",
216			static_cast<unsigned long>(B_STYLED_TEXT_FORMAT));
217
218	printf("header size: %u ",
219		static_cast<unsigned int>(stxtheader.header.header_size));
220	if (stxtheader.header.header_size == kstxtsize)
221		printf("(valid)\n");
222	else
223		printf("(INVALID, should be %u)\n",
224			static_cast<unsigned int>(kstxtsize));
225
226	printf("data size: %u ",
227		static_cast<unsigned int>(stxtheader.header.data_size));
228	if (stxtheader.header.data_size == 0)
229		printf("(valid)\n");
230	else
231		printf("(INVALID, should be 0)\n");
232
233	printf("version: %d ",
234		static_cast<int>(stxtheader.version));
235	if (stxtheader.version == 100)
236		printf("(valid)\n");
237		else
238		printf("(INVALID, should be 100)\n");
239
240
241	// Check the TEXT header
242	TranslatorStyledTextTextHeader txtheader;
243	if (file.Read(buffer, ktxtsize) != static_cast<ssize_t>(ktxtsize)) {
244		printf("Error: Unable to read text header\n");
245		return false;
246	}
247	memcpy(&txtheader, buffer, ktxtsize);
248	if (swap_data(B_UINT32_TYPE, &txtheader, ktxtsize,
249		B_SWAP_BENDIAN_TO_HOST) != B_OK) {
250		printf("Error: Unable to swap byte order of text header\n");
251		return false;
252	}
253
254	// Print Text Header (TEXT)
255	printf("\nText Header (TEXT section):\n\n");
256
257	printf("magic number: 0x%.8lx ", txtheader.header.magic);
258	if (txtheader.header.magic == 'TEXT')
259		printf("(valid)\n");
260	else
261		printf("(INVALID, should be 0x%.8lx)\n",
262			static_cast<unsigned long>('TEXT'));
263
264	printf("header size: %u ",
265		static_cast<unsigned int>(txtheader.header.header_size));
266	if (stxtheader.header.header_size == ktxtsize)
267		printf("(valid)\n");
268	else
269		printf("(INVALID, should be %u)\n",
270			static_cast<unsigned int>(ktxtsize));
271
272	printf("data size (bytes of text): %u\n",
273		static_cast<unsigned int>(txtheader.header.data_size));
274
275	printf("character set: %d ",
276		static_cast<int>(txtheader.charset));
277	if (txtheader.charset == B_UNICODE_UTF8)
278		printf("(valid)\n");
279	else
280		printf("(INVALID, should be %d)\n", B_UNICODE_UTF8);
281
282	// Skip the text data
283	off_t seekresult, pos;
284	pos = stxtheader.header.header_size +
285		txtheader.header.header_size +
286		txtheader.header.data_size;
287	seekresult = file.Seek(txtheader.header.data_size, SEEK_CUR);
288	if (seekresult < pos) {
289		printf("Error: Unable to seek past text data. " \
290			"Text data could be missing\n");
291		return false;
292	}
293	if (seekresult > pos) {
294		printf("Error: File position is beyond expected value\n");
295		return false;
296	}
297
298	// Check the STYL header (not all STXT files have this)
299	ssize_t read = 0;
300	TranslatorStyledTextStyleHeader stylheader;
301	read = file.Read(buffer, kstylsize);
302	if (read != static_cast<ssize_t>(kstylsize) && read != 0) {
303		printf("Error: Unable to read entire style header\n");
304		return false;
305	}
306
307	// If there is no STYL header (and no errors)
308	if (read != static_cast<ssize_t>(kstylsize)) {
309		printf("\nFile contains no Style Header (STYL section)\n");
310		return false;
311	}
312
313	memcpy(&stylheader, buffer, kstylsize);
314	if (swap_data(B_UINT32_TYPE, &stylheader, kstylsize,
315		B_SWAP_BENDIAN_TO_HOST) != B_OK) {
316		printf("Error: Unable to swap byte order of style header\n");
317		return false;
318	}
319
320	// Print Style Header (STYL)
321	printf("\nStyle Header (STYL section):\n\n");
322
323	printf("magic number: 0x%.8lx ", stylheader.header.magic);
324	if (stylheader.header.magic == 'STYL')
325		printf("(valid)\n");
326	else
327		printf("(INVALID, should be 0x%.8lx)\n",
328			static_cast<unsigned long>('STYL'));
329
330	printf("header size: %u ",
331		static_cast<unsigned int>(stylheader.header.header_size));
332	if (stylheader.header.header_size == kstylsize)
333		printf("(valid)\n");
334	else
335		printf("(INVALID, should be %u)\n",
336			static_cast<unsigned int>(kstylsize));
337
338	printf("data size: %u\n",
339		static_cast<unsigned int>(stylheader.header.data_size));
340	printf("apply offset: %u (usually 0)\n",
341		static_cast<unsigned int>(stylheader.apply_offset));
342	printf("apply length: %u (usually the text data size)\n",
343		static_cast<unsigned int>(stylheader.apply_length));
344
345	// Check Styles
346	StylesHeader stylesheader;
347	read = file.Read(buffer, sizeof(StylesHeader));
348	if (read != sizeof(StylesHeader)) {
349		printf("Error: Unable to read Styles header\n");
350		return false;
351	}
352	memcpy(&stylesheader, buffer, sizeof(StylesHeader));
353	if (swap_data(B_UINT32_TYPE, &stylesheader, sizeof(StylesHeader),
354		B_SWAP_BENDIAN_TO_HOST) != B_OK) {
355		printf("Error: Unable to swap byte order of styles header\n");
356		return false;
357	}
358
359	// Print StylesHeader info
360	printf("\nStyles Header (Ali! section):\n\n");
361
362	printf("magic number: 0x%.8lx ",
363		static_cast<unsigned long>(stylesheader.magic));
364	if (stylesheader.magic == 'Ali!')
365		printf("(valid)\n");
366	else
367		printf("(INVALID, should be 0x%.8lx)\n",
368			static_cast<unsigned long>('Ali!'));
369
370	printf("version: 0x%.8lx ",
371		static_cast<unsigned long>(stylesheader.version));
372	if (stylesheader.version == 0)
373		printf("(valid)\n");
374	else
375		printf("(INVALID, should be 0x%.8lx)\n", 0UL);
376
377	printf("number of styles: %d\n",
378		static_cast<int>(stylesheader.count));
379
380	// Check and Print out each style
381	for (int32 i = 0; i < stylesheader.count; i++) {
382		Style style;
383		read = file.Read(&style, sizeof(Style));
384		if (read != sizeof(Style)) {
385			printf("Error: Unable to read style %d\n",
386				static_cast<int>(i + 1));
387			return false;
388		}
389
390		PrintStyle(style, i);
391	}
392
393	return true;
394}
395
396int
397main(int argc, char **argv)
398{
399	printf("\n");
400
401	if (argc == 2) {
402		BFile file(argv[1], B_READ_ONLY);
403		if (file.InitCheck() != B_OK)
404			printf("Error opening %s\n", argv[1]);
405		else {
406			printf("Be styled text information for: %s\n\n", argv[1]);
407			if (PrintStylesAttribute(file) == false) {
408				printf("Unable to read styles attribute, attempting to read " \
409					"style information from file...\n\n");
410				PrintStxtInfo(file);
411			}
412		}
413	}
414	else {
415		printf("stxtinfo - reports information about a Be styled text file\n");
416		printf("\nUsage:\n");
417		printf("stxtinfo filename.stxt\n");
418	}
419
420	printf("\n");
421
422	return 0;
423}
424
425