1 /*
2  * $LynxId: HTStyle.c,v 1.16 2009/11/27 13:01:48 tom Exp $
3  *
4  *	Style Implementation for Hypertext			HTStyle.c
5  *	==================================
6  *
7  *	Styles allow the translation between a logical property
8  *	of a piece of text and its physical representation.
9  *
10  *	A StyleSheet is a collection of styles, defining the
11  *	translation necessary to
12  *	represent a document.  It is a linked list of styles.
13  */
14 
15 #include <HTUtils.h>
16 #include <HTStyle.h>
17 
18 #include <LYLeaks.h>
19 
20 /*	Create a new style
21 */
HTStyleNew(void)22 HTStyle *HTStyleNew(void)
23 {
24     HTStyle *self = typecalloc(HTStyle);
25 
26     if (self == NULL)
27 	outofmem(__FILE__, "HTStyleNew");
28     return self;
29 }
30 
31 /*	Create a new style with a name
32 */
HTStyleNewNamed(const char * name)33 HTStyle *HTStyleNewNamed(const char *name)
34 {
35     HTStyle *self = HTStyleNew();
36 
37     StrAllocCopy(self->w_name, name);
38     self->id = -1;		/* <0 */
39     return self;
40 }
41 
42 /*	Free a style
43 */
HTStyleFree(HTStyle * self)44 HTStyle *HTStyleFree(HTStyle *self)
45 {
46     FREE(self->w_name);
47     FREE(self->w_SGMLTag);
48     FREE(self);
49     return NULL;
50 }
51 
52 #ifdef SUPPRESS			/* Only on the NeXT */
53 /*	Read a style from a stream	(without its name)
54  *	--------------------------
55  *
56  *	Reads a style with paragraph information from a stream.
57  *	The style name is not read or written by these routines.
58  */
59 #define NONE_STRING "(None)"
60 #define HTStream NXStream
61 
HTStyleRead(HTStyle * style,HTStream * stream)62 HTStyle *HTStyleRead(HTStyle *style, HTStream *stream)
63 {
64     char myTag[STYLE_NAME_LENGTH];
65     char fontName[STYLE_NAME_LENGTH];
66     NXTextStyle *p;
67     int tab;
68     int gotpara;		/* flag: have we got a paragraph definition? */
69 
70     NXScanf(stream, "%s%s%f%d",
71 	    myTag,
72 	    fontName,
73 	    &style->fontSize,
74 	    &gotpara);
75     if (gotpara) {
76 	if (!style->paragraph) {
77 	    style->paragraph = malloc(sizeof(*(style->paragraph)));
78 	    if (!style->paragraph)
79 		outofmem(__FILE__, "HTStyleRead");
80 	    style->paragraph->tabs = 0;
81 	}
82 	p = style->paragraph;
83 	NXScanf(stream, "%f%f%f%f%hd%f%f%hd",
84 		&p->indent1st,
85 		&p->indent2nd,
86 		&p->lineHt,
87 		&p->descentLine,
88 		&p->alignment,
89 		&style->spaceBefore,
90 		&style->spaceAfter,
91 		&p->numTabs);
92 	FREE(p->tabs);
93 	p->tabs = malloc(p->numTabs * sizeof(p->tabs[0]));
94 	if (!p->tabs)
95 	    outofmem(__FILE__, "HTStyleRead");
96 	for (tab = 0; tab < p->numTabs; tab++) {
97 	    NXScanf(stream, "%hd%f",
98 		    &p->tabs[tab].kind,
99 		    &p->tabs[tab].x);
100 	}
101     } else {			/* No paragraph */
102 	FREE(style->paragraph);
103     }				/* if no paragraph */
104     StrAllocCopy(style->SGMLTag, myTag);
105     if (strcmp(fontName, NONE_STRING) == 0)
106 	style->font = 0;
107     else
108       style->font =[Font newFont: fontName size:style->fontSize];
109     return NULL;
110 }
111 
112 /*	Write a style to a stream in a compatible way
113 */
HTStyleWrite(HTStyle * style,NXStream * stream)114 HTStyle *HTStyleWrite(HTStyle *style, NXStream * stream)
115 {
116     int tab;
117     NXTextStyle *p = style->paragraph;
118 
119     NXPrintf(stream, "%s %s %f %d\n",
120 	     style->SGMLTag,
121 	     style->font ?[style->font name] : NONE_STRING,
122 	     style->fontSize,
123 	     p != 0);
124 
125     if (p) {
126 	NXPrintf(stream, "\t%f %f %f %f %d %f %f\t%d\n",
127 		 p->indent1st,
128 		 p->indent2nd,
129 		 p->lineHt,
130 		 p->descentLine,
131 		 p->alignment,
132 		 style->spaceBefore,
133 		 style->spaceAfter,
134 		 p->numTabs);
135 
136 	for (tab = 0; tab < p->numTabs; tab++)
137 	    NXPrintf(stream, "\t%d %f\n",
138 		     p->tabs[tab].kind,
139 		     p->tabs[tab].x);
140     }
141     return style;
142 }
143 
144 /*	Write a style to stdout for diagnostics
145 */
HTStyleDump(HTStyle * style)146 HTStyle *HTStyleDump(HTStyle *style)
147 {
148     int tab;
149     NXTextStyle *p = style->paragraph;
150 
151     printf(STYLE_DUMP_FONT,
152 	   style,
153 	   style->name,
154 	   style->SGMLTag,
155 	   [style->font name],
156 	   style->fontSize);
157     if (p) {
158 	printf(STYLE_DUMP_IDENT,
159 	       p->indent1st,
160 	       p->indent2nd,
161 	       p->lineHt,
162 	       p->descentLine);
163 	printf(STYLE_DUMP_ALIGN,
164 	       p->alignment,
165 	       p->numTabs,
166 	       style->spaceBefore,
167 	       style->spaceAfter);
168 
169 	for (tab = 0; tab < p->numTabs; tab++) {
170 	    printf(STYLE_DUMP_TAB,
171 		   p->tabs[tab].kind,
172 		   p->tabs[tab].x);
173 	}
174 	printf("\n");
175     }				/* if paragraph */
176     return style;
177 }
178 #endif /* SUPPRESS */
179 
180 /*			StyleSheet Functions
181  *			====================
182  */
183 
184 /*	Searching for styles:
185 */
HTStyleNamed(HTStyleSheet * self,const char * name)186 HTStyle *HTStyleNamed(HTStyleSheet *self, const char *name)
187 {
188     HTStyle *scan;
189 
190     for (scan = self->styles; scan; scan = scan->next)
191 	if (0 == strcmp(GetHTStyleName(scan), name))
192 	    return scan;
193     CTRACE((tfp, "StyleSheet: No style named `%s'\n", name));
194     return NULL;
195 }
196 
197 #ifdef NEXT_SUPRESS		/* Not in general common code */
198 
HTStyleMatching(HTStyleSheet * self,HTStyle * style)199 HTStyle *HTStyleMatching(HTStyleSheet *self, HTStyle *style)
200 {
201     HTStyle *scan;
202 
203     for (scan = self->styles; scan; scan = scan->next)
204 	if (scan->paragraph == para)
205 	    return scan;
206     return NULL;
207 }
208 
209 /*	Find the style which best fits a given run
210  *	------------------------------------------
211  *
212  *	This heuristic is used for guessing the style for a run of
213  *	text which has been pasted in.  In order, we try:
214  *
215  *	A style whose paragraph structure is actually used by the run.
216  *	A style matching in font
217  *	A style matching in paragraph style exactly
218  *	A style matching in paragraph to a degree
219  */
220 
HTStyleForRun(HTStyleSheet * self,NXRun * run)221 HTStyle *HTStyleForRun(HTStyleSheet *self, NXRun * run)
222 {
223     HTStyle *scan;
224     HTStyle *best = 0;
225     int bestMatch = 0;
226     NXTextStyle *rp = run->paraStyle;
227 
228     for (scan = self->styles; scan; scan = scan->next)
229 	if (scan->paragraph == run->paraStyle)
230 	    return scan;	/* Exact */
231 
232     for (scan = self->styles; scan; scan = scan->next) {
233 	NXTextStyle *sp = scan->paragraph;
234 
235 	if (sp) {
236 	    int match = 0;
237 
238 	    if (sp->indent1st == rp->indent1st)
239 		match = match + 1;
240 	    if (sp->indent2nd == rp->indent2nd)
241 		match = match + 2;
242 	    if (sp->lineHt == rp->lineHt)
243 		match = match + 1;
244 	    if (sp->numTabs == rp->numTabs)
245 		match = match + 1;
246 	    if (sp->alignment == rp->alignment)
247 		match = match + 3;
248 	    if (scan->font == run->font)
249 		match = match + 10;
250 	    if (match > bestMatch) {
251 		best = scan;
252 		bestMatch = match;
253 	    }
254 	}
255     }
256     CTRACE((tfp, "HTStyleForRun: Best match for style is %d out of 18\n",
257 	    bestMatch));
258     return best;
259 }
260 #endif /* NEXT_SUPRESS */
261 
262 /*	Add a style to a sheet
263  *	----------------------
264  */
HTStyleSheetAddStyle(HTStyleSheet * self,HTStyle * style)265 HTStyleSheet *HTStyleSheetAddStyle(HTStyleSheet *self, HTStyle *style)
266 {
267     style->next = 0;		/* The style will go on the end */
268     if (!self->styles) {
269 	self->styles = style;
270     } else {
271 	HTStyle *scan;
272 
273 	for (scan = self->styles; scan->next; scan = scan->next) ;	/* Find end */
274 	scan->next = style;
275     }
276     return self;
277 }
278 
279 /*	Remove the given object from a style sheet if it exists
280 */
HTStyleSheetRemoveStyle(HTStyleSheet * self,HTStyle * style)281 HTStyleSheet *HTStyleSheetRemoveStyle(HTStyleSheet *self, HTStyle *style)
282 {
283     if (self->styles == style) {
284 	self->styles = style->next;
285 	return self;
286     } else {
287 	HTStyle *scan;
288 
289 	for (scan = self->styles; scan; scan = scan->next) {
290 	    if (scan->next == style) {
291 		scan->next = style->next;
292 		return self;
293 	    }
294 	}
295     }
296     return NULL;
297 }
298 
299 /*	Create new style sheet
300 */
301 
HTStyleSheetNew(void)302 HTStyleSheet *HTStyleSheetNew(void)
303 {
304     HTStyleSheet *self = typecalloc(HTStyleSheet);
305 
306     if (self == NULL)
307 	outofmem(__FILE__, "HTStyleSheetNew");
308     return self;
309 }
310 
311 /*	Free off a style sheet pointer
312 */
HTStyleSheetFree(HTStyleSheet * self)313 HTStyleSheet *HTStyleSheetFree(HTStyleSheet *self)
314 {
315     HTStyle *style;
316 
317     while ((style = self->styles) != 0) {
318 	self->styles = style->next;
319 	HTStyleFree(style);
320     }
321     FREE(self);
322     return NULL;
323 }
324 
325 /*	Read a stylesheet from a typed stream
326  *	-------------------------------------
327  *
328  *	Reads a style sheet from a stream.  If new styles have the same names
329  *	as existing styles, they replace the old ones without changing the ids.
330  */
331 
332 #ifdef NEXT_SUPRESS		/* Only on the NeXT */
HTStyleSheetRead(HTStyleSheet * self,NXStream * stream)333 HTStyleSheet *HTStyleSheetRead(HTStyleSheet *self, NXStream * stream)
334 {
335     int numStyles;
336     int i;
337     HTStyle *style;
338     char styleName[80];
339 
340     NXScanf(stream, " %d ", &numStyles);
341     CTRACE((tfp, "Stylesheet: Reading %d styles\n", numStyles));
342     for (i = 0; i < numStyles; i++) {
343 	NXScanf(stream, "%s", styleName);
344 	style = HTStyleNamed(self, styleName);
345 	if (!style) {
346 	    style = HTStyleNewNamed(styleName);
347 	    (void) HTStyleSheetAddStyle(self, style);
348 	}
349 	(void) HTStyleRead(style, stream);
350 	if (TRACE)
351 	    HTStyleDump(style);
352     }
353     return self;
354 }
355 
356 /*	Write a stylesheet to a typed stream
357  *	------------------------------------
358  *
359  *	Writes a style sheet to a stream.
360  */
361 
HTStyleSheetWrite(HTStyleSheet * self,NXStream * stream)362 HTStyleSheet *HTStyleSheetWrite(HTStyleSheet *self, NXStream * stream)
363 {
364     int numStyles = 0;
365     HTStyle *style;
366 
367     for (style = self->styles; style; style = style->next)
368 	numStyles++;
369     NXPrintf(stream, "%d\n", numStyles);
370 
371     CTRACE((tfp, "StyleSheet: Writing %d styles\n", numStyles));
372     for (style = self->styles; style; style = style->next) {
373 	NXPrintf(stream, "%s ", style->name);
374 	(void) HTStyleWrite(style, stream);
375     }
376     return self;
377 }
378 #endif /* NEXT_SUPRESS */
379