1 use loc::Loc;
2 use focus_char::FocusChar;
3 use optimizer::Optimizer;
4 use svg_element::SvgElement;
5 use svg::node::element::SVG;
6 use svg::Node;
7 use svg::node::element::{
8 Definitions,
9 Marker,
10 Rectangle as SvgRect,
11 Style,
12 Circle as SvgCircle,
13 Polygon as SvgPolygon,
14 Group,
15 };
16 use element::Element;
17 use pom::TextInput;
18 use pom::parser::{sym,none_of};
19 use settings::Settings;
20 use unicode_width::UnicodeWidthChar;
21 use pom;
22
23 #[derive(Debug)]
24 pub struct Grid {
25 pub settings: Settings,
26 /// cell value is in string instead of char to accomodate multiple width characters
27 /// each line is Vec<String>
28 index: Vec<Vec<String>>,
29 /// This are text elements that are escaped and are not processed for diagram
30 /// matching
31 text_elm: Vec<(usize, usize, String)>,
32 }
33 impl Grid {
34 /// instantiate a grid from input ascii text
35 /// Issues:
36 /// 1. 2-width, 5 bytes, single character i.e. 统
37 /// 2. 1-width, 2 bytes, single character i.e. ö
38 /// 3. 1-width, 3 bytes, single character i.e. o͡͡͡
from_str(s: &str, settings: &Settings) -> Grid39 pub fn from_str(s: &str, settings: &Settings) -> Grid {
40 let lines: Vec<&str> = s.lines().collect();
41 let mut rows: Vec<Vec<String>> = Vec::with_capacity(lines.len());
42 let mut text_elm: Vec<(usize, usize, String)> = vec![];
43 for (y, line) in lines.iter().enumerate() {
44 let (line, escaped_texts): (String, Vec<(usize, String)>) = exclude_escaped_text(line);
45 let mut row: Vec<String> = Vec::with_capacity(line.chars().count());
46 for (x, escaped) in escaped_texts {
47 text_elm.push((x, y, svg_escape(&escaped)));
48 }
49 for ch in line.chars() {
50 if let Some(1) = ch.width() {
51 row.push(format!("{}", ch));
52 } else if let Some(2) = ch.width() {
53 row.push(format!("{}", ch));
54 // HACK: push a blank to the next cell,
55 //in order to make this character twice as
56 // big and aligns the next succeeding characters on
57 // this row
58 row.push(format!("\0"));
59 }
60 // if zero width char, append it to the previous string
61 else if let Some(0) = ch.width() {
62 let prev: Option<String> = row.pop();
63 match prev {
64 Some(mut prev) => {
65 prev.push(ch);
66 row.push(prev);
67 }
68 None => (),
69 }
70 }
71 }
72 rows.push(row);
73 }
74 let g = Grid {
75 settings: settings.clone(),
76 index: rows,
77 text_elm: text_elm,
78 };
79 // do the pre processing here
80 g.pre_process()
81 }
82
83
rows(&self) -> usize84 pub fn rows(&self) -> usize {
85 self.index.len()
86 }
87
88 /// get the maximum row len
columns(&self) -> usize89 pub fn columns(&self) -> usize {
90 self.index.iter().map(|r| r.len()).max().unwrap_or(0)
91 }
92
93 /// get a character at this location
94 /// widths are computed since there are
95 /// characters that spans 2 columns
96 /// and characters that has 0 width
97 ///
get(&self, loc: &Loc) -> Option<&String>98 pub fn get(&self, loc: &Loc) -> Option<&String> {
99 match self.index.get(loc.y as usize) {
100 Some(row) => row.get(loc.x as usize),
101 None => None,
102 }
103 }
104
105
text(&self, loc: &Loc) -> &str106 fn text(&self, loc: &Loc) -> &str {
107 match self.get(loc) {
108 Some(ref s) => s,
109 None => ""
110 }
111 }
112
113 /// get the focus char at this location
get_focuschar(&self, loc: &Loc) -> FocusChar114 pub fn get_focuschar(&self, loc: &Loc) -> FocusChar {
115 FocusChar::new(&loc, self)
116 }
117
118 /// process the enhancing of circle elements
119 /// this should be called before other elements are extracted from the grid
get_enhance_circle_elements(&self) -> (Vec<Vec<Vec<Element>>>, Vec<Loc>)120 fn get_enhance_circle_elements(&self) -> (Vec<Vec<Vec<Element>>>, Vec<Loc>){
121 let mut rows: Vec<Vec<Vec<Element>>> = Vec::with_capacity(self.index.len());
122 let mut all_consumed_loc: Vec<Loc> = vec![];
123 for (y,line) in self.index.iter().enumerate() {
124 let mut row: Vec<Vec<Element>> = Vec::with_capacity(line.len());
125 for (x,_cell) in line.iter().enumerate() {
126 let loc = Loc::new(x as i32, y as i32);
127 let focus_char = self.get_focuschar(&loc);
128 let (cell_elements, consumed_loc) = focus_char.get_enhance_circle_elements();
129 all_consumed_loc.extend(consumed_loc);
130 row.push(cell_elements);
131 }
132 rows.push(row);
133 }
134 (rows, all_consumed_loc)
135 }
136
137 /// process the enhanced circle elements first
138 /// then process the generic enhancements
get_enhance_elements(&self) -> (Vec<Vec<Vec<Element>>>, Vec<Loc>)139 fn get_enhance_elements(&self) -> (Vec<Vec<Vec<Element>>>, Vec<Loc>){
140 let (enhanced_circle_elm, circle_consumed_loc) = self.get_enhance_circle_elements();
141 let mut rows: Vec<Vec<Vec<Element>>> = Vec::with_capacity(self.index.len());
142 rows.extend(enhanced_circle_elm);
143 let mut all_consumed_loc: Vec<Loc> = vec![];
144 for (y,line) in self.index.iter().enumerate() {
145 let mut row: Vec<Vec<Element>> = Vec::with_capacity(line.len());
146 for (x,_cell) in line.iter().enumerate() {
147 let loc = Loc::new(x as i32, y as i32);
148 if !circle_consumed_loc.contains(&loc){
149 let focus_char = self.get_focuschar(&loc);
150 let (cell_elements, consumed_loc) = focus_char.get_enhance_elements();
151 all_consumed_loc.extend(consumed_loc);
152 row.push(cell_elements);
153 }
154 }
155 rows.push(row);
156 }
157 all_consumed_loc.extend(circle_consumed_loc);
158 (rows, all_consumed_loc)
159 }
160
161 /// vector of each elements arranged in rows x columns
162 /// returns all the elements and the consumed location
get_all_elements(&self) -> Vec<Vec<Vec<Element>>>163 fn get_all_elements(&self) -> Vec<Vec<Vec<Element>>>{
164 let (enhanced_elms, enhance_consumed_locs) = self.get_enhance_elements();
165 let mut rows: Vec<Vec<Vec<Element>>> = Vec::with_capacity(self.index.len());
166 rows.extend(enhanced_elms);
167 for (y, line) in self.index.iter().enumerate() {
168 let mut row: Vec<Vec<Element>> = Vec::with_capacity(line.len());
169 for (x, _cell) in line.iter().enumerate() {
170 let loc = Loc::new(x as i32, y as i32);
171 if !enhance_consumed_locs.contains(&loc) {
172 let focus_char = self.get_focuschar(&loc);
173 let cell_elements = focus_char.get_elements();
174 row.push(cell_elements);
175 }
176 }
177 rows.push(row);
178 }
179 rows
180 }
181
182
get_escaped_text_elements(&self) -> Vec<Element>183 fn get_escaped_text_elements(&self) -> Vec<Element> {
184 self.text_elm
185 .iter()
186 .map(|&(x, y, ref text)| Element::Text(Loc::new(x as i32, y as i32), text.to_owned()))
187 .collect()
188 }
189
190 /// each component has its relative location retain
191 /// use this info for optimizing svg by checking closest neigbor
get_svg_nodes(&self) -> Vec<Vec<SvgElement>>192 fn get_svg_nodes(&self) -> Vec<Vec<SvgElement>> {
193 let mut grouped_nodes = vec![];
194 let mut elements= self.get_all_elements();
195 let text_elm = self.get_escaped_text_elements();
196 elements.push(vec![text_elm]);
197 let optimizer = Optimizer::new(elements);
198 let optimized_elements:Vec<Vec<Element>> = optimizer.optimize(&self.settings);
199 for group in optimized_elements {
200 let mut svg_group = vec![];
201 for elem in group{
202 let element: SvgElement = elem.to_svg(&self.settings);
203 svg_group.push(element);
204 }
205 grouped_nodes.push(svg_group);
206 }
207 grouped_nodes
208 }
209
210
get_size(&self) -> (f32, f32)211 pub fn get_size(&self) -> (f32, f32) {
212 let width = self.settings.text_width * self.columns() as f32;
213 let height = self.settings.text_height * self.rows() as f32;
214 (width, height)
215 }
216
217 /// get the generated svg according to the settings specified
get_svg(&self) -> SVG218 pub fn get_svg(&self) -> SVG {
219 let group_nodes = self.get_svg_nodes();
220 let (width, height) = self.get_size();
221 let mut svg = SVG::new();
222
223 if let Some(ref id) = self.settings.id {
224 svg.assign("id", id.to_owned());
225 }
226 if let Some(ref class) = self.settings.class {
227 svg.assign("class", class.to_owned());
228 }
229 svg.assign("font-size", self.settings.font_size);
230 svg.assign("font-family", self.settings.font_family.to_owned());
231 svg.assign("width", width);
232 svg.assign("height", height);
233
234
235 svg.append(get_defs());
236 svg.append(get_styles(&self.settings));
237
238
239 let rect = SvgRect::new()
240 .set("x", 0)
241 .set("y", 0)
242 .set("class","backdrop")
243 .set("width", width)
244 .set("height", height);
245 svg.append(rect);
246
247 for group in group_nodes {
248 let mut svg_group = Group::new();
249 for node in group{
250 match node {
251 SvgElement::Circle(circle) => {
252 svg_group.append(circle);
253 }
254 SvgElement::Line(line) => {
255 svg_group.append(line);
256 }
257 SvgElement::Path(path) => {
258 svg_group.append(path);
259 }
260 SvgElement::Text(text) => {
261 svg_group.append(text);
262 }
263 }
264 }
265 svg.append(svg_group);
266 }
267 svg
268 }
269 /// traverse each element of the grid and swap characters as needed
pre_process(&self) -> Self270 fn pre_process(&self) -> Self {
271 let mut new_index: Vec<Vec<String>> = vec![];
272 for (y,line) in self.index.iter().enumerate(){
273 let mut row: Vec<String> = vec![];
274 for (x,_) in line.iter().enumerate(){
275 let loc = &Loc::new(x as i32, y as i32);
276 let swap = self.swap_char(loc);
277 row.push(swap.to_string());
278 }
279 new_index.push(row);
280 }
281 Grid{
282 settings: self.settings.clone(),
283 index: new_index,
284 text_elm: self.text_elm.clone()
285 }
286 }
287
288 /// swap characters - - - with ~~~~~
swap_char(&self, loc: &Loc) -> &str289 fn swap_char(&self, loc: &Loc) -> &str {
290 let cell = self.text(loc);
291
292 let left = self.text(&loc.left());
293 let left2 = self.text(&loc.in_left(2));
294 let left3 = self.text(&loc.in_left(3));
295 let left4 = self.text(&loc.in_left(4));
296 let right = self.text(&loc.right());
297 let right2 = self.text(&loc.in_right(2));
298 let right3 = self.text(&loc.in_right(3));
299 let right4 = self.text(&loc.in_right(4));
300 // [- - -]
301 // ^
302 // if `-` and right is ` ` and right(2) is `-` right(3) is ` ` and right(4) is `-`
303 if (cell == "-" && right == " " && right2 == "-" && right3 == " " && right4 == "-")
304 // [- - -]
305 // ^
306 // if ` ` and left is `-` and right is `-` and right(2) is ` ` and right(3) is `-`
307 || (cell == " " && left == "-" && right == "-" && right2 == " " && right3 == "-")
308 // [- - -]
309 // ^
310 // if `-` , and left is ` ` and right is ` ` and left(2) is `-` and right(2) is `-`
311 || (cell == "-" && left == " " && right == " " && left2 == "-" && right2 == "-")
312 // [- - -]
313 // ^
314 // if ` `, and left is `-` and right is `-` and left(2) is ` ` and left(3) is `-`
315 || (cell == " " && left == "-" && right == "-" && left2 == " " && left3 == "-")
316 // [- - -]
317 // ^
318 // if `-`, and left is ` ` and left(2) is `-` and left(3) is ` ` and left(4) is `-`
319 || (cell == "-" && left == " " && left2 == "-" && left3 == " " && left4 == "-")
320 {
321 "~"
322 }
323 else{
324 cell
325 }
326 }
327
328 }
329
get_defs() -> Definitions330 fn get_defs() -> Definitions {
331 let mut defs = Definitions::new();
332 defs.append(arrow_marker());
333 defs.append(clear_arrow_marker());
334 defs.append(circle_marker());
335 defs.append(square_marker());
336 defs.append(open_circle_marker());
337 defs.append(big_open_circle_marker());
338 defs
339 }
340
get_styles(settings: &Settings) -> Style341 fn get_styles(settings: &Settings) -> Style {
342 let style = format!(
343 r#"
344 rect.backdrop {{
345 fill: {background_color};
346 }}
347 text{{
348 fill: {stroke_color};
349 }}
350
351 circle {{
352 fill: none;
353 stroke: {stroke_color};
354 stroke-width: {stroke_width};
355 }}
356
357 line {{
358 stroke: {stroke_color};
359 stroke-width: {stroke_width};
360 stroke-opacity: 1;
361 fill-opacity: 1;
362 stroke-linecap: round;
363 stroke-linejoin: miter;
364 }}
365
366 path {{
367 fill: none;
368 stroke: {stroke_color};
369 stroke-width: {stroke_width};
370 stroke-opacity: 1;
371 fill-opacity: 1;
372 stroke-linecap: round;
373 stroke-linejoin: miter;
374 }}
375
376 line.dashed {{
377 stroke-dasharray: 5;
378 }}
379
380 .fg_fill {{
381 fill: {stroke_color};
382 }}
383
384
385 .bg_fill {{
386 fill: {background_color};
387 stroke: {stroke_color};
388 stroke-width: {stroke_width};
389 }}
390
391 tspan.head{{
392 fill: none;
393 stroke: none;
394 }}
395 "#,
396 stroke_width = settings.stroke_width,
397 stroke_color = &settings.stroke_color,
398 background_color = &settings.background_color,
399 );
400 Style::new(style)
401 .set("type", "text/css")
402 }
403
arrow_marker() -> Marker404 fn arrow_marker() -> Marker {
405 let mut marker = Marker::new()
406 .set("id", "triangle")
407 .set("viewBox", "0 0 8 4")
408 .set("refX", 4)
409 .set("refY", 2)
410 .set("orient", "auto")
411 .set("markerWidth", 8)
412 .set("markerHeight", 8);
413
414 let path = SvgPolygon::new()
415 .set("points", "0,0 0,4 8,2 0,0")
416 .set("class", "fg_fill");
417
418 marker.append(path);
419 marker
420 }
421
clear_arrow_marker() -> Marker422 fn clear_arrow_marker() -> Marker {
423 let mut marker = Marker::new()
424 .set("id", "clear_triangle")
425 .set("viewBox", "0 0 20 14")
426 .set("refX", 1)
427 .set("refY", 7)
428 .set("orient", "auto")
429 .set("markerWidth", 10)
430 .set("markerHeight", 10);
431
432 let path = SvgPolygon::new()
433 .set("points", "2,2 2,12 18,7 2,2")
434 .set("class", "bg_fill");
435
436 marker.append(path);
437 marker
438 }
439
440
441 /// <marker id="dot" viewBox="0 0 10 10" refX="5" refY="5"
442 /// markerWidth="5" markerHeight="5">
443 /// <circle cx="5" cy="5" r="5" fill="red" />
444 /// </marker>
circle_marker() -> Marker445 fn circle_marker() -> Marker {
446 let mut marker = Marker::new()
447 .set("id", "circle")
448 .set("viewBox", "0 0 20 20")
449 .set("refX", 10)
450 .set("refY", 10)
451 .set("orient", "auto")
452 .set("markerWidth", 5)
453 .set("markerHeight", 5);
454
455 let circle = SvgCircle::new()
456 .set("cx",10)
457 .set("cy",10)
458 .set("r",8)
459 .set("class", "fg_fill");
460 marker.append(circle);
461 marker
462 }
463
square_marker() -> Marker464 fn square_marker() -> Marker {
465 let mut marker = Marker::new()
466 .set("id", "square")
467 .set("viewBox", "0 0 20 20")
468 .set("refX", 10)
469 .set("refY", 10)
470 .set("orient", "auto")
471 .set("markerWidth", 5)
472 .set("markerHeight", 5);
473
474 let square = SvgRect::new()
475 .set("x",0)
476 .set("y",0)
477 .set("width",20)
478 .set("height",20)
479 .set("class", "fg_fill");
480 marker.append(square);
481 marker
482 }
483
open_circle_marker() -> Marker484 fn open_circle_marker() -> Marker {
485 let mut marker = Marker::new()
486 .set("id", "open_circle")
487 .set("viewBox", "0 0 20 20")
488 .set("refX", 10)
489 .set("refY", 10)
490 .set("orient", "auto")
491 .set("markerWidth", 10)
492 .set("markerHeight", 10);
493
494 let circle = SvgCircle::new()
495 .set("cx",10)
496 .set("cy",10)
497 .set("r",4)
498 .set("class", "bg_fill");
499 marker.append(circle);
500 marker
501 }
big_open_circle_marker() -> Marker502 fn big_open_circle_marker() -> Marker {
503 let mut marker = Marker::new()
504 .set("id", "big_open_circle")
505 .set("viewBox", "0 0 40 40")
506 .set("refX", 20)
507 .set("refY", 20)
508 .set("orient", "auto")
509 .set("markerWidth", 20)
510 .set("markerHeight", 20);
511
512 let circle = SvgCircle::new()
513 .set("cx",20)
514 .set("cy",20)
515 .set("r",6)
516 .set("class", "bg_fill");
517 marker.append(circle);
518 marker
519 }
520 //copied from https://github.com/rust-lang/rust/blob/master/src/librustdoc/html/escape.rs
521 //just adding for \0
svg_escape(arg: &str) -> String522 pub fn svg_escape(arg: &str) -> String {
523 use std::fmt;
524
525 /// Wrapper struct which will emit the HTML-escaped version of the contained
526 /// string when passed to a format string.
527 pub struct Escape<'a>(pub &'a str);
528
529 impl<'a> fmt::Display for Escape<'a> {
530 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
531 // Because the internet is always right, turns out there's not that many
532 // characters to escape: http://stackoverflow.com/questions/7381974
533 let Escape(s) = *self;
534 let pile_o_bits = s;
535 let mut last = 0;
536 for (i, ch) in s.bytes().enumerate() {
537 match ch as char {
538 '<' | '>' | '&' | '\'' | '"' | '\0' => {
539 fmt.write_str(&pile_o_bits[last..i])?;
540 let s = match ch as char {
541 '>' => ">",
542 '<' => "<",
543 '&' => "&",
544 '\'' => "'",
545 '"' => """,
546 '\0' => "",
547 _ => unreachable!(),
548 };
549 fmt.write_str(s)?;
550 last = i + 1;
551 }
552 _ => {}
553 }
554 }
555
556 if last < s.len() {
557 fmt.write_str(&pile_o_bits[last..])?;
558 }
559 Ok(())
560 }
561 };
562 let escaped = Escape(arg);
563 format!("{}", escaped)
564 }
565
exclude_escaped_text(line: &str) -> (String, Vec<(usize, String)>)566 fn exclude_escaped_text(line: &str) -> (String, Vec<(usize, String)>) {
567 let mut input = TextInput::new(line);
568 let parsed = line_parse().parse(&mut input);
569 let mut buffer = String::new();
570 let mut text_elm: Vec<(usize, String)> = vec![];
571 if let Ok(parsed) = parsed {
572 let mut index = 0;
573 if !parsed.is_empty() {
574 for (start, end) in parsed {
575 let escaped = &line[start + 1..end];
576 let recons = &line[index..start];
577 text_elm.push((start, escaped.to_string()));
578 buffer.push_str(recons);
579 buffer.push_str(&" ".repeat(end + 1 - start));
580 index = end + 1;
581 }
582 buffer.push_str(&line[index..line.len()]);
583 } else {
584 buffer.push_str(line);
585 }
586 }
587 (buffer, text_elm)
588 }
589
590 #[test]
test_escaped_string()591 fn test_escaped_string() {
592 let input3 = r#"The "qu/i/ck" brown "fox\"s" jumps over the lazy "do|g""#;
593 let mut raw3 = TextInput::new(input3);
594 let output3 = line_parse().parse(&mut raw3);
595 println!("output3: {:?}", output3);
596 //assert_eq!(Ok(vec![(4, 12), (20, 27), (49, 54)]), output3);
597 let mut matches = vec![];
598 let mut recons = String::new();
599 let mut text_elm: Vec<(usize, String)> = vec![];
600 let mut index = 0;
601 if let Ok(output) = output3 {
602 for (start, end) in output {
603 println!("matches: {}", &input3[start..end + 1]);
604 matches.push(input3[start..end + 1].to_string());
605 let slice = &input3[index..start];
606 recons.push_str(slice);
607 recons.push_str(&" ".repeat(end + 1 - start));
608 text_elm.push((start, input3[start + 1..end].to_string()));
609 index = end + 1;
610 }
611 }
612 println!("input3: {}", input3);
613 println!("recons: {}", recons);
614 println!("escaped: {:?}", text_elm);
615 assert_eq!(vec![r#""qu/i/ck""#, r#""fox\"s""#, r#""do|g""#], matches);
616 assert_eq!(input3.len(), recons.len());
617 }
618
619 #[test]
test_escaped_multiline_string()620 fn test_escaped_multiline_string() {
621 let input3 = r#"The "qu/i/ck brown fox \njumps over the lazy do|g""#;
622 let mut raw3 = TextInput::new(input3);
623 let output3 = line_parse().parse(&mut raw3);
624 println!("output3: {:?}", output3);
625 assert_eq!(Ok(vec![(4, 49)]), output3);
626 let mut matches = vec![];
627 let mut recons = String::new();
628 let mut text_elm: Vec<(usize, String)> = vec![];
629 let mut index = 0;
630 if let Ok(output) = output3 {
631 for (start, end) in output {
632 println!("matches: {}", &input3[start..end + 1]);
633 matches.push(input3[start..end + 1].to_string());
634 let slice = &input3[index..start];
635 recons.push_str(slice);
636 recons.push_str(&" ".repeat(end + 1 - start));
637 text_elm.push((start, input3[start + 1..end].to_string()));
638 index = end + 1;
639 }
640 }
641 println!("input3: {}", input3);
642 println!("recons: {}", recons);
643 println!("escaped: {:?}", text_elm);
644 assert_eq!(
645 vec![r#""qu/i/ck brown fox \njumps over the lazy do|g""#],
646 matches
647 );
648 assert_eq!(input3.len(), recons.len());
649 }
650
escape_string() -> pom::parser::Parser<'static, char, (usize, usize)>651 fn escape_string() -> pom::parser::Parser<'static, char, (usize, usize)> {
652 let escape_sequence = sym('\\') * sym('"'); //escape sequence \"
653 let char_string = escape_sequence | none_of("\"");
654 let escaped_string_end = sym('"') * char_string.repeat(0..).pos() - sym('"');
655 none_of("\"").repeat(0..).pos() + escaped_string_end - none_of("\"").repeat(0..).discard()
656 }
657
line_parse() -> pom::parser::Parser<'static, char, Vec<(usize, usize)>>658 fn line_parse() -> pom::parser::Parser<'static, char, Vec<(usize, usize)>> {
659 escape_string().repeat(0..)
660 }
661
662