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                             '>' => "&gt;",
542                             '<' => "&lt;",
543                             '&' => "&amp;",
544                             '\'' => "&#39;",
545                             '"' => "&quot;",
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