Repository: jdrouet/mrml (language: rust)

Issue:
Default fonts does not seems to behave as a fallback
I just discover this awesome lib, thanks for the great work!

Redefining in template one of the default -or configured- font seems to duplicate definition instead of replacing it. for instance with `Roboto`

```xml
<mjml>
  <mj-head>
    <mj-font name="Roboto" href="https://fonts.googleapis.com/css?family=Roboto:100,100i,300,300i,400,400i,500,500i,700,700i,900,900i" />
  </mj-head>
  <mj-body>
    <mj-section>
      <mj-column>
        <mj-text font-family="Roboto, Arial">
          Hello World!
        </mj-text>
      </mj-column>
    </mj-section>
  </mj-body>
</mjml>
```
produces
```xml
<link href="https://fonts.googleapis.com/css?family=Roboto:100,100i,300,300i,400,400i,500,500i,700,700i,900,900i" rel="stylesheet" type="text/css">
<style type="text/css">
  @import url(https://fonts.googleapis.com/css?family=Roboto:100,100i,300,300i,400,400i,500,500i,700,700i,900,900i);
</style>
```
using MJML,
but duplicates the definitions with MRML:
```xml
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700" rel="stylesheet" type="text/css">
<link href="https://fonts.googleapis.com/css?family=Roboto:100,100i,300,300i,400,400i,500,500i,700,700i,900,900i" rel="stylesheet" type="text/css">
<style type="text/css">
  @import url(https://fonts.googleapis.com/css?family=Roboto:300,400,500,700);
  @import url(https://fonts.googleapis.com/css?family=Roboto:100,100i,300,300i,400,400i,500,500i,700,700i,900,900i);
</style>
```


I am not a rust developer, but a simple fix could look like
```diff
diff --git a/packages/mrml-core/src/mj_head/render.rs b/packages/mrml-core/src/mj_head/render.rs
index d377bdc..9b2f617 100644
--- a/packages/mrml-core/src/mj_head/render.rs
+++ b/packages/mrml-core/src/mj_head/render.rs
@@ -145,23 +145,25 @@ impl<'e, 'h> MjHeadRender<'e, 'h> {
         header
             .used_font_families()
             .iter()
-            .filter_map(|name| opts.fonts.get(name))
+            .filter_map(|name| header.font_families().get(name.as_str()))
             .for_each(|href| links.push_str(&self.render_font_link(href)));
         header
             .used_font_families()
             .iter()
-            .filter_map(|name| header.font_families().get(name.as_str()))
+            .filter(|name| !header.font_families().get(name.as_str()).is_some())
+            .filter_map(|name| opts.fonts.get(name))
             .for_each(|href| links.push_str(&self.render_font_link(href)));
         let mut imports = String::default();
         header
             .used_font_families()
             .iter()
-            .filter_map(|name| opts.fonts.get(name))
+            .filter_map(|name| header.font_families().get(name.as_str()))
             .for_each(|href| imports.push_str(&self.render_font_import(href)));
         header
             .used_font_families()
             .iter()
-            .filter_map(|name| header.font_families().get(name.as_str()))
+            .filter(|name| !header.font_families().get(name.as_str()).is_some())
+            .filter_map(|name| opts.fonts.get(name))
             .for_each(|href| imports.push_str(&self.render_font_import(href)));
         if links.is_empty() && imports.is_empty() {
             String::default()
```



Discussion / hints:
Thanks for this 👍 Will fix it asap
could you tell me how you use it? you might just have to update the default fonts with an empty map
I am planning to use [ruby/rails integration](https://github.com/hardpixel/mrml-ruby/blob/master/ext/mrml/src/lib.rs) but I did use the cli for reproduction by comparing html outputs between mrml and mjml :
```bash
cat font.mjml | mrml render > font.mrml.html
cat font.mjml | mjml -is > font.mjml.html
# e.g. npm install -g html-differ && html-differ font.mjml.html font.mrml.html
```

Fix patch:
diff --git a/packages/mrml-core/src/mj_head/render.rs b/packages/mrml-core/src/mj_head/render.rs
index 53d9938d..ffa7a001 100644
--- a/packages/mrml-core/src/mj_head/render.rs
+++ b/packages/mrml-core/src/mj_head/render.rs
@@ -179,48 +179,45 @@ impl MjHead {
     }
 }
 
+fn render_font_import(target: &mut String, href: &str) {
+    target.push_str("@import url(");
+    target.push_str(href);
+    target.push_str(");");
+}
+
+fn render_font_link(target: &mut String, href: &str) {
+    target.push_str("<link href=\"");
+    target.push_str(href);
+    target.push_str("\" rel=\"stylesheet\" type=\"text/css\">");
+}
+
 pub struct MjHeadRender<'e, 'h> {
     header: Rc<RefCell<Header<'h>>>,
     element: &'e MjHead,
 }
 
 impl<'e, 'h> MjHeadRender<'e, 'h> {
-    fn render_font_import(&self, href: &str) -> String {
-        format!("@import url({href});")
-    }
-
-    fn render_font_link(&self, href: &str) -> String {
-        format!("<link href=\"{href}\" rel=\"stylesheet\" type=\"text/css\">")
-    }
-
     fn render_font_families(&self, opts: &RenderOptions) -> String {
         let header = self.header.borrow();
         let used_font_families = header.used_font_families();
         if used_font_families.is_empty() {
             return String::default();
         }
+
         let mut links = String::default();
-        header
-            .used_font_families()
-            .iter()
-            .filter_map(|name| opts.fonts.get(name))
-            .for_each(|href| links.push_str(&self.render_font_link(href)));
-        header
-            .used_font_families()
-            .iter()
-            .filter_map(|name| header.font_families().get(name.as_str()))
-            .for_each(|href| links.push_str(&self.render_font_link(href)));
         let mut imports = String::default();
-        header
-            .used_font_families()
-            .iter()
-            .filter_map(|name| opts.fonts.get(name))
-            .for_each(|href| imports.push_str(&self.render_font_import(href)));
-        header
-            .used_font_families()
-            .iter()
-            .filter_map(|name| header.font_families().get(name.as_str()))
-            .for_each(|href| imports.push_str(&self.render_font_import(href)));
+        for name in header.used_font_families().iter() {
+            if let Some(href) = header.font_families().get(name.as_str()) {
+                render_font_link(&mut links, href);
+                render_font_import(&mut imports, href);
+            } else if let Some(href) = opts.fonts.get(name) {
+                render_font_link(&mut links, href);
+                render_font_import(&mut imports, href);
+            } else {
+                // TODO log a warning
+            }
+        }
+
         if links.is_empty() && imports.is_empty() {
             String::default()
         } else {


Test patch:
diff --git a/packages/mrml-core/tests/multiple-mj-font.rs b/packages/mrml-core/tests/multiple-mj-font.rs
new file mode 100644
index 00000000..4f1450de
--- /dev/null
+++ b/packages/mrml-core/tests/multiple-mj-font.rs
@@ -0,0 +1,23 @@
+#![cfg(feature = "parse")]
+
+use mrml::prelude::render::RenderOptions;
+
+#[test]
+fn should_have_a_single_roboto_font_imported() {
+    let parsed = mrml::parse(r#"<mjml>
+  <mj-head>
+    <mj-font name="Roboto" href="https://fonts.googleapis.com/css?family=Roboto:100,100i,300,300i,400,400i,500,500i,700,700i,900,900i" />
+    </mj-head>
+    <mj-body>
+    <mj-section>
+        <mj-column>
+        <mj-text font-family="Roboto, Arial">
+            Hello World!
+        </mj-text>
+        </mj-column>
+    </mj-section>
+    </mj-body>
+</mjml>"#).unwrap();
+    let rendered = parsed.render(&RenderOptions::default()).unwrap();
+    assert!(!rendered.contains("Roboto:300,400,500,700"));
+}