diff --git a/crates/engine/src/cmd/paste.rs b/crates/engine/src/cmd/paste.rs
--- a/crates/engine/src/cmd/paste.rs
+++ b/crates/engine/src/cmd/paste.rs
@@ -1,12 +1,15 @@
-//! Paste Special: paste the clipboard in a chosen format (formatted, RTF, HTML, picture,
+//! Paste Special: paste the clipboard in a chosen format (formatted, Markdown, RTF, HTML, picture,
 //! unformatted), and the formatted content Paste and Merge Formatting take from other programs.
 //!
 //! The clipboard can hold several representations of the same content. WordCraft's own copy keeps
 //! a rich fragment; the system clipboard gives text, and callers (agents, the CLI, a front end
-//! with a richer clipboard) may pass `html`, `rtf` or a `picture` too. Text that is itself RTF or
-//! HTML source can also be pasted as that format. Paste keeps the source formatting of another
-//! program's HTML or RTF; Merge Formatting keeps only its emphasis (#687).
-//! Plain text has no formatting to keep, so Paste inserts it as Keep Text Only does.
+//! with a richer clipboard) may pass `html`, `rtf` or a `picture` too. Text that is itself Markdown,
+//! RTF or HTML source can also be pasted as that format. Paste keeps the source formatting of
+//! another program's HTML or RTF; Merge Formatting keeps only its emphasis (#687).
+//! Text with Markdown structure (headings, lists, tables, emphasis, code) reads as a document, as
+//! opening a `.md` file does, so pasting it gives its headings and lists WordCraft's own
+//! formatting instead of the source's punctuation; plain text has no formatting to keep, so Paste
+//! inserts it as Keep Text Only does.
 
 use std::collections::BTreeMap;
 use std::sync::Arc;
@@ -26,8 +29,9 @@ pub const MAX_PICTURE_BYTES: usize = 96 << 20;
 const MAX_DEPTH: usize = 64;
 
 /// The formats, in the order the dialog lists them: (id, label).
-pub const FORMATS: [(&str, &str); 5] = [
+pub const FORMATS: [(&str, &str); 6] = [
     ("formatted", "Formatted Text"),
+    ("markdown", "Markdown"),
     ("rtf", "Rich Text Format (RTF)"),
     ("html", "HTML Format"),
     ("picture", "Picture"),
@@ -35,11 +39,9 @@ pub const FORMATS: [(&str, &str); 5] = [
 ];
 
 pub fn specs() -> Vec<CommandSpec> {
-    vec![
-        CommandSpec::new("edit.pasteSpecial", "Paste Special", "Home › Clipboard › Paste", paste_special)
-            .key("Mod+Alt+V")
-            .params(r#"{"as"?: "formatted"|"rtf"|"html"|"picture"|"text", "text"?: string, "html"?: string, "rtf"?: string, "picture"?: base64}"#),
-    ]
+    vec![CommandSpec::new("edit.pasteSpecial", "Paste Special", "Home › Clipboard › Paste", paste_special).key("Mod+Alt+V").params(
+        r#"{"as"?: "formatted"|"markdown"|"rtf"|"html"|"picture"|"text", "text"?: string, "html"?: string, "rtf"?: string, "picture"?: base64}"#,
+    )]
 }
 
 /// Clipboard text from the params (system clipboard), else WordCraft's own copy.
@@ -59,6 +61,201 @@ fn looks_like_html(t: &str) -> bool {
     t.starts_with('<') && (t.contains("</") || t.contains("/>"))
 }
 
+/// How much of a pasted text [`looks_like_markdown`] reads, so a hostile clipboard can't make the
+/// test long.
+const MARKDOWN_SCAN_BYTES: usize = 64 << 10;
+/// Most lines [`looks_like_markdown`] reads.
+const MARKDOWN_SCAN_LINES: usize = 500;
+
+/// The first `max` bytes of `t` (fewer if that would cut a character).
+fn head_bytes(t: &str, max: usize) -> &str {
+    if t.len() <= max {
+        return t;
+    }
+    let mut end = max;
+    while end > 0 && !t.is_char_boundary(end) {
+        end -= 1;
+    }
+    t.get(..end).unwrap_or("")
+}
+
+/// A line without Markdown's indentation: up to three spaces. More than that, or a tab, starts an
+/// indented code block, whose lines are code and not structure.
+fn unindented(l: &str) -> &str {
+    let mut s = l;
+    for _ in 0..3 {
+        match s.strip_prefix(' ') {
+            Some(rest) => s = rest,
+            None => break,
+        }
+    }
+    s
+}
+
+/// The level of an ATX heading line (`## Tema`), if it is one.
+fn atx_heading(l: &str) -> Option<u8> {
+    let s = unindented(l);
+    let n = s.chars().take_while(|&c| c == '#').count();
+    if n == 0 || n > 6 {
+        return None;
+    }
+    let rest = s.get(n..)?;
+    (rest.is_empty() || rest.starts_with([' ', '\t'])).then_some(n as u8)
+}
+
+/// The fence character of a code fence line (```` ``` ```` or `~~~`), if it is one.
+fn fence_marker(l: &str) -> Option<char> {
+    let s = unindented(l);
+    let c = s.chars().next()?;
+    if c != '`' && c != '~' {
+        return None;
+    }
+    (s.chars().take_while(|&x| x == c).count() >= 3).then_some(c)
+}
+
+/// Whether the line is a thematic break (`---`, `***`, `___`).
+fn thematic_break(l: &str) -> bool {
+    let s = unindented(l).trim_end_matches([' ', '\t']);
+    let Some(c) = s.chars().next() else {
+        return false;
+    };
+    if !matches!(c, '-' | '*' | '_') {
+        return false;
+    }
+    s.chars().filter(|&x| x == c).count() >= 3 && s.chars().all(|x| x == c || x == ' ')
+}
+
+/// Whether the line is a block quote line (`> quoted`).
+fn block_quote(l: &str) -> bool {
+    let s = unindented(l);
+    s == ">" || s.starts_with("> ") || s.starts_with(">\t")
+}
+
+/// Whether the line is a GFM table's delimiter row (`| --- | :-: |`).
+fn table_delimiter(l: &str) -> bool {
+    let s = unindented(l).trim();
+    if !s.contains('|') {
+        return false;
+    }
+    let cells: Vec<&str> = s.trim_matches('|').split('|').map(str::trim).collect();
+    cells.len() >= 2
+        && cells.iter().all(|c| {
+            let c = c.strip_prefix(':').unwrap_or(c);
+            let c = c.strip_suffix(':').unwrap_or(c);
+            !c.is_empty() && c.chars().all(|x| x == '-')
+        })
+}
+
+/// Whether the line is an unordered list item (`- a`).
+fn bullet_item(l: &str) -> bool {
+    let s = unindented(l);
+    let Some(c) = s.chars().next() else {
+        return false;
+    };
+    if !matches!(c, '-' | '*' | '+') {
+        return false;
+    }
+    let rest = s.get(1..).unwrap_or("");
+    matches!(rest.chars().next(), Some(' ') | Some('\t')) && !rest.trim().is_empty()
+}
+
+/// Whether the line is an ordered list item (`1. a`).
+fn ordered_item(l: &str) -> bool {
+    let s = unindented(l);
+    let digits = s.chars().take_while(char::is_ascii_digit).count();
+    if digits == 0 || digits > 9 {
+        return false;
+    }
+    let rest = s.get(digits..).unwrap_or("");
+    let mut it = rest.chars();
+    if !matches!(it.next(), Some('.') | Some(')')) {
+        return false;
+    }
+    matches!(it.next(), Some(' ') | Some('\t')) && !it.as_str().trim().is_empty()
+}
+
+/// Whether the line is a setext heading's underline (`===`).
+fn setext_underline(l: &str) -> bool {
+    let s = unindented(l).trim_end_matches([' ', '\t']);
+    s.len() >= 2 && s.chars().all(|c| c == '=')
+}
+
+/// Emphasis, code and link markers in `t`, as pairs: `**bold**` is one, a link one, a code span
+/// one.
+fn inline_markers(t: &str) -> usize {
+    let count = |m: &str| t.matches(m).count();
+    let double = |m: &str| count(m) / 2;
+    double("**") + double("__") + double("~~") + count("`") / 2 + count("](")
+}
+
+/// Whether clipboard text reads as a Markdown document rather than as prose: the punctuation
+/// someone typed as text (`#`, `**`, `- `, `|`), which, pasted, becomes WordCraft's own headings,
+/// lists, tables and emphasis, as opening a `.md` file does.
+///
+/// The test errs towards "no": one heading-looking line (a `#` comment in a shell snippet), one
+/// `>` quote or one pair of `**` is text, and Keep Text Only pastes it raw. What it accepts: a
+/// code fence, a GFM table, two headings, a heading beside emphasis, three list items, and so on.
+/// Paste Special still offers Markdown and formats any text the user picks, whatever this says.
+fn looks_like_markdown(t: &str) -> bool {
+    if t.trim().is_empty() || t.len() > MAX_PASTE_BYTES {
+        return false;
+    }
+    let head = head_bytes(t, MARKDOWN_SCAN_BYTES);
+    let mut headings = 0usize;
+    let mut fences = 0usize;
+    let mut tables = 0usize;
+    let mut quotes = 0usize;
+    let mut rules = 0usize;
+    let mut bullets = 0usize;
+    let mut ordered = 0usize;
+    let mut fence: Option<char> = None;
+    for line in head.split('\n').take(MARKDOWN_SCAN_LINES) {
+        if let Some(c) = fence {
+            // Inside a code fence the lines are code: only the closing fence reads.
+            if fence_marker(line) == Some(c) {
+                fences += 1;
+                fence = None;
+            }
+            continue;
+        }
+        if let Some(c) = fence_marker(line) {
+            fences += 1;
+            fence = Some(c);
+        } else if atx_heading(line).is_some() {
+            headings += 1;
+        } else if table_delimiter(line) {
+            tables += 1;
+        } else if block_quote(line) {
+            quotes += 1;
+        } else if thematic_break(line) {
+            rules += 1;
+        } else if bullet_item(line) {
+            bullets += 1;
+        } else if ordered_item(line) {
+            ordered += 1;
+        } else if setext_underline(line) {
+            headings += 1;
+        }
+    }
+    let structural = headings + fences + tables + quotes + rules;
+    let lists = bullets + ordered;
+    let inline = inline_markers(head);
+    // A code block, a table or a second heading is Markdown whatever else the text holds.
+    if fences >= 2 || tables > 0 || headings >= 2 {
+        return true;
+    }
+    // A heading over emphasized text (`## Tema` and `**nota**`) is a document.
+    if headings > 0 && inline >= 2 {
+        return true;
+    }
+    // Three list items are a list, not three lines that happen to begin with a dash.
+    if bullets >= 3 {
+        return true;
+    }
+    // Anything else that adds up: a quote and two items, a rule and bold and a link, …
+    structural > 0 && structural + lists + inline >= 3
+}
+
 /// The source for a format, if the clipboard has it.
 fn source(s: &Session, v: &Value, fmt: &str) -> Option<String> {
     let non_empty = |t: String| if t.is_empty() { None } else { Some(t) };
@@ -73,6 +270,9 @@ fn source(s: &Session, v: &Value, fmt: &str) -> Option<String> {
         }
         "rtf" => p::str(v, "rtf").map(str::to_string).or_else(|| clip_text(s, v).filter(|t| looks_like_rtf(t))).and_then(non_empty),
         "html" => p::str(v, "html").map(str::to_string).or_else(|| clip_text(s, v).filter(|t| looks_like_html(t))).and_then(non_empty),
+        // Picking Markdown formats any text, as picking HTML parses HTML source; only the dialog's
+        // list of formats (see `available`) is what the test keeps off text that isn't Markdown.
+        "markdown" => clip_text(s, v).filter(|t| !t.trim().is_empty() && t.len() <= MAX_PASTE_BYTES),
         "picture" => p::str(v, "picture").map(str::to_string).and_then(non_empty),
         "text" => clip_text(s, v).and_then(non_empty),
         _ => None,
@@ -81,7 +281,16 @@ fn source(s: &Session, v: &Value, fmt: &str) -> Option<String> {
 
 /// Formats the clipboard can be pasted as, best first.
 pub fn available(s: &Session, v: &Value) -> Vec<&'static str> {
-    FORMATS.iter().map(|(id, _)| *id).filter(|id| source(s, v, id).is_some()).collect()
+    FORMATS
+        .iter()
+        .map(|(id, _)| *id)
+        .filter(|id| match *id {
+            // Offered for text that reads as Markdown, not for every text: plain prose is what Keep
+            // Text Only is for, and the dialog shouldn't suggest turning it into a document.
+            "markdown" => clip_text(s, v).is_some_and(|t| looks_like_markdown(&t)),
+            _ => source(s, v, id).is_some(),
+        })
+        .collect()
 }
 
 fn formats_json(ids: &[&str]) -> Value {
@@ -113,11 +322,14 @@ fn paste_special(s: &mut Session, v: &Value) -> CmdResult {
     };
     let fmt = match fmt.to_ascii_lowercase().as_str() {
         "formatted" | "keep" | "wordcraft" => "formatted",
+        "markdown" | "md" => "markdown",
         "rtf" => "rtf",
         "html" => "html",
         "picture" | "png" | "image" => "picture",
         "text" | "unformatted" | "plain" => "text",
-        other => return Err(CmdError::Params(format!("unknown format `{other}` (formatted, rtf, html, picture or text)"))),
+        other => {
+            return Err(CmdError::Params(format!("unknown format `{other}` (formatted, markdown, rtf, html, picture or text)")));
+        }
     };
     let Some(src) = source(s, v, fmt) else {
         return Err(CmdError::Failed(if ids.is_empty() {
@@ -139,6 +351,7 @@ fn paste_special(s: &mut Session, v: &Value) -> CmdResult {
             return Ok(r);
         }
         "formatted" => s.clipboard.clone().ok_or_else(|| CmdError::Failed("the clipboard is empty".into()))?,
+        "markdown" => adopt(s, wordcraft_formats::markdown::import(&src)),
         "html" => adopt(s, import_html(&src)),
         _ => adopt(s, wordcraft_formats::rtf::import(src.as_bytes()).map_err(|e| CmdError::Failed(format!("bad RTF: {e}")))?),
     };
@@ -259,14 +472,16 @@ pub fn rich_fragment(s: &mut Session, v: &Value) -> Option<Fragment> {
 
 /// What a Paste brings in.
 pub enum Pasted {
-    /// WordCraft's own copy, or another program's HTML or RTF: it keeps the formatting it carries.
+    /// WordCraft's own copy, or another program's HTML, RTF or Markdown: it keeps the formatting
+    /// it carries.
     Fragment(Fragment),
     /// The system clipboard's text, lines separated by `\n`.
     Text(String),
 }
 
 /// What Paste inserts: WordCraft's own copy while the system clipboard still holds it, else
-/// another program's formatted copy (HTML, RTF), else the system clipboard's text.
+/// another program's formatted copy (HTML, RTF), else Markdown that reads as a document, else the
+/// system clipboard's text.
 pub fn paste_content(s: &mut Session, v: &Value) -> Result<Pasted, CmdError> {
     let text = p::str(v, "text").map(|t| t.replace("\r\n", "\n"));
     if let Some(f) = &s.clipboard
@@ -278,6 +493,15 @@ pub fn paste_content(s: &mut Session, v: &Value) -> Result<Pasted, CmdError> {
     if let Some(f) = rich_fragment(s, v) {
         return Ok(Pasted::Fragment(f));
     }
+    if let Some(t) = text.as_deref().filter(|t| looks_like_markdown(t)) {
+        // Markdown that reads as a document: its headings, lists, tables and emphasis become this
+        // document's formatting, as opening a `.md` file does. Keep Text Only pastes it as text,
+        // and so does Paste Special › Unformatted Text.
+        let doc = wordcraft_formats::markdown::import(t);
+        if !doc.body.is_empty() {
+            return Ok(Pasted::Fragment(adopt(s, doc)));
+        }
+    }
     match text {
         Some(t) if !t.is_empty() => Ok(Pasted::Text(t)),
         _ => Err(CmdError::Failed("the clipboard is empty".into())),
diff --git a/crates/engine/src/tests.rs b/crates/engine/src/tests.rs
--- a/crates/engine/src/tests.rs
+++ b/crates/engine/src/tests.rs
@@ -1673,6 +1673,127 @@ fn paste_keeps_source_formatting_from_other_programs() {
     assert_eq!(props_at(&s, 4).italic, Some(true));
 }
 
+/// Markdown copied as text reads as a document when pasted: its headings, lists, tables and
+/// emphasis become WordCraft's own formatting, as opening a `.md` file does — instead of the
+/// source's `##`, `**` and `- ` landing in the text.
+#[test]
+fn paste_reads_markdown_as_a_formatted_document() {
+    let mut s = s();
+    let md =
+        "# Título\n\n## Tema central da questão\n\n**Direito** e *ênfase*\n\n- A\n- B\n\n> citação\n\n---\n\n| A | B |\n| :-: | --- |\n| 1 | 2 |\n";
+    run(&mut s, "edit.paste", json!({"text": md}));
+    let plain = text(&s);
+    assert!(plain.starts_with("Título\nTema central da questão\nDireito e ênfase"), "{plain:?}");
+    for marker in ["#", "**", "*ênfase*", "- A", "| ---"] {
+        assert!(!plain.contains(marker), "{marker:?} left in {plain:?}");
+    }
+    let style = |s: &Session, start: &str| {
+        s.doc.body.iter().filter_map(|b| b.as_para()).find(|p| p.text.starts_with(start)).and_then(|p| p.props.style.clone())
+    };
+    assert_eq!(style(&s, "Título").as_deref(), Some("Heading1"));
+    assert_eq!(style(&s, "Tema central").as_deref(), Some("Heading2"));
+    assert_eq!(style(&s, "citação").as_deref(), Some("Quote"));
+    // Emphasis: bold on "Direito", italic on "ênfase".
+    let at = s.doc.body.iter().position(|b| b.as_para().is_some_and(|p| p.text.starts_with("Direito"))).unwrap();
+    assert!(bold_at(&s, at, 0));
+    assert_eq!(s.doc.para_at(&Pos::body(at, 10)).unwrap().props_of_char(10).italic, Some(true));
+    // A bullet list, with its numbering definition adopted.
+    let lists: Vec<_> = s.doc.body.iter().filter_map(|b| b.as_para()).filter(|p| p.props.numbering.is_some_and(|n| n.num != 0)).collect();
+    assert_eq!(lists.len(), 2, "{plain:?}");
+    assert!(lists.iter().all(|p| s.doc.numbering.nums.iter().any(|n| n.id == p.props.numbering.unwrap().num)));
+    // The pipe table is a table.
+    assert!(s.doc.body.iter().any(|b| matches!(&**b, wordcraft_doc::Block::Table(_))), "{plain:?}");
+}
+
+/// Paste Special offers Markdown for text that reads as Markdown, formats that text (and any text
+/// the user picks Markdown for), and doesn't offer it for prose.
+#[test]
+fn paste_special_lists_and_pastes_markdown() {
+    let mut s = s();
+    let md = "## Tema\n\n**negrito** e lista:\n\n- um\n- dois\n- três\n";
+    let r = run(&mut s, "edit.pasteSpecial", json!({"text": md}));
+    let ids: Vec<&str> = r["formats"].as_array().unwrap().iter().map(|f| f["id"].as_str().unwrap()).collect();
+    assert_eq!(ids, ["markdown", "text"]);
+    let r = run(&mut s, "edit.pasteSpecial", json!({"as": "markdown", "text": md}));
+    assert_eq!(r["pastedAs"], "markdown");
+    assert!(text(&s).starts_with("Tema\nnegrito e lista:\num\ndois\ntrês"), "{}", text(&s));
+    assert_eq!(s.doc.body.first().and_then(|b| b.as_para()).and_then(|p| p.props.style.as_deref()), Some("Heading2"));
+    // Picking Markdown formats text the detection would leave alone: one pair of `**` is enough
+    // once the user says what the text is.
+    let mut t = self::s();
+    run(&mut t, "edit.pasteSpecial", json!({"as": "markdown", "text": "usa **negrito** aqui"}));
+    assert_eq!(text(&t), "usa negrito aqui");
+    assert!(bold_at(&t, 0, 4));
+    // Prose: Markdown is not offered.
+    let r = run(&mut t, "edit.pasteSpecial", json!({"text": "Art. 1º O Município é pessoa jurídica de direito público interno."}));
+    let ids: Vec<&str> = r["formats"].as_array().unwrap().iter().map(|f| f["id"].as_str().unwrap()).collect();
+    assert_eq!(ids, ["text"]);
+}
+
+/// Text that only looks a little like Markdown stays text: one `#` comment line, one `>` quote or
+/// one pair of `**` is prose, not a document, and so are numbered lines with no other structure.
+#[test]
+fn markdown_detection_keeps_prose_as_text() {
+    let offered = |sample: &str| {
+        let mut s = s();
+        let r = run(&mut s, "edit.pasteSpecial", json!({"text": sample}));
+        r["formats"].as_array().unwrap().iter().any(|f| f["id"] == "markdown")
+    };
+    for sample in ["# git status\ngit status --short", "Use **cuidado** ao citar o art. 5º.", "> ver nota anterior", "1. Art. 5º\n2. Art. 6º"] {
+        assert!(!offered(sample), "read as Markdown: {sample:?}");
+        let mut s = s();
+        run(&mut s, "edit.paste", json!({"text": sample}));
+        assert_eq!(text(&s), sample, "pasted as text: {sample:?}");
+        assert!(s.doc.body.iter().filter_map(|b| b.as_para()).all(|p| p.props.style.is_none() && p.props.numbering.is_none()), "{sample:?}");
+    }
+    for sample in ["# Um\n\n# Dois", "```\ncode\n```", "| A | B |\n| - | - |\n| 1 | 2 |", "- a\n- b\n- c", "## Tema\n\n**negrito** e `código`"] {
+        assert!(offered(sample), "not read as Markdown: {sample:?}");
+    }
+}
+
+/// Keep Text Only keeps the Markdown source, whatever Paste would make of it.
+#[test]
+fn paste_text_keeps_the_markdown_source() {
+    let mut s = s();
+    let md = "## Tema\n\n**negrito**\n\n- um\n- dois\n- três\n";
+    run(&mut s, "edit.pasteText", json!({"text": md}));
+    assert_eq!(text(&s), md);
+    assert!(s.doc.body.iter().filter_map(|b| b.as_para()).all(|p| p.props.style.is_none() && p.props.numbering.is_none()), "{}", text(&s));
+    // A statute pasted as text: numbered lines are its text, not a list to renumber.
+    let law = "Art. 1º O Município é pessoa jurídica de direito público interno.\n\n1. A autonomia política;\n2. A autonomia financeira;\n\n* Ver a Lei Orgânica.";
+    let mut t = self::s();
+    run(&mut t, "edit.paste", json!({"text": law}));
+    assert_eq!(text(&t), law);
+    assert!(t.doc.body.iter().filter_map(|b| b.as_para()).all(|p| p.props.numbering.is_none()));
+}
+
+/// Headings pasted from Markdown keep their styles, outline levels and emoji through a save and
+/// a reopen: the Navigator derives its tree from those levels, so the outline survives the file.
+#[test]
+fn pasted_headings_keep_their_outline_and_emoji_after_saving() {
+    let mut s = s();
+    run(&mut s, "edit.paste", json!({"text": "## 1️⃣ Tema central\n\n### 📘 Conceito Básico\n\n## 2️⃣ Resumo teórico\n"}));
+    assert_eq!(text(&s), "1️⃣ Tema central\n📘 Conceito Básico\n2️⃣ Resumo teórico");
+    let dir = std::env::temp_dir().join(format!("wc-outline-{}", std::process::id()));
+    std::fs::create_dir_all(&dir).unwrap();
+    let path = dir.join("outline.docx");
+    run(&mut s, "file.saveAs", json!({"path": path.to_str().unwrap()}));
+    let mut t = self::s();
+    run(&mut t, "file.open", json!({"path": path.to_str().unwrap()}));
+    let outline: Vec<Option<u8>> = t
+        .doc
+        .body
+        .iter()
+        .filter_map(|b| b.as_para())
+        .filter(|p| p.text.trim() != "")
+        .map(|p| t.doc.styles.resolve_para(&p.props).outline_level)
+        .collect();
+    assert_eq!(outline, [Some(1), Some(2), Some(1)], "Heading 2, Heading 3, Heading 2");
+    assert!(text(&t).contains("1\u{FE0F}\u{20E3}"), "the keycap code points survive the file: {:?}", text(&t));
+    assert!(text(&t).contains("📘"), "the marker emoji survives the file: {:?}", text(&t));
+    let _ = std::fs::remove_dir_all(&dir);
+}
+
 /// A CF_HTML ("HTML Format") clipboard payload around `html`, with the byte offsets Windows writes.
 fn cf_html(html: &str) -> String {
     let n = "Version:1.0\r\nStartHTML:0000000000\r\nEndHTML:0000000000\r\nStartFragment:0000000000\r\nEndFragment:0000000000\r\n".len();
