chiark / gitweb /
svg size handling: Introduce new size parser
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Mon, 2 May 2022 09:56:57 +0000 (10:56 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Mon, 2 May 2022 09:56:57 +0000 (10:56 +0100)
We want to get the size out of the svg on load, to make the library
spec size attribute optional (and to make things less confusing).
So start by add code to parse size out of an SVG.

But in fact it turns out we have one of these in bundle processing
already.  The need to be combined.  But for now, commit what we have.

Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
src/shapelib.rs
src/spec.rs

index 5a0c47119cc4617744209fd13c4e1ebea53f9340..9d555418893f7072a951fde7723e6dae6ca4493c 100644 (file)
@@ -520,6 +520,15 @@ impl ItemSpec {
   }
 }
 
+#[derive(Error,Clone,Serialize,Deserialize,Debug)]
+pub enum SVGSizeError {
+  #[error("parse error: {0}")]           ParseError(String),
+  #[error("attribute {0} repeated")]     AttributeRepeated(String),
+  #[error("attribute {0} unparseable")]  AttributeUnparseable(String),
+  #[error("specifies only one of width and height")] OneOfWidthHeight,
+}
+use SVGSizeError as SvSE;
+
 impl Contents {
   #[throws(SpecError)]
   fn load_svg(&self, item_name: &SvgBaseName<str>,
@@ -538,6 +547,47 @@ impl Contents {
         SpE::InternalError(m.to_string())
       })?;
 
+    #[throws(SVGSizeError)]
+    fn get_width_height(xml: &str) -> Option<PosC<f64>> {
+      use xmlparser::Token as Tk;
+      let mut in_svg_element = false;
+      let mut wh = [None; 2];
+      for token in xmlparser::Tokenizer::from(xml) {
+        match token.map_err(|e| SvSE::ParseError(e.to_string()))? {
+          Tk::ElementStart{ local, .. } => {
+            in_svg_element = local.eq_ignore_ascii_case("svg");
+          },
+          Tk::ElementEnd{..} => {
+            if in_svg_element { return None }
+          },
+          Tk::Attribute { local, value, .. } if in_svg_element => {
+            let i =
+              if local.eq_ignore_ascii_case("width" ) { 0 } else
+              if local.eq_ignore_ascii_case("height") { 1 } else { continue };
+            if wh[i].is_some() {
+              throw!(SvSE::AttributeRepeated(local.to_string()))
+            }
+            let v: f64 = value.parse().map_err(
+              |_| SvSE::AttributeUnparseable(local.to_string())
+            )?;
+            wh[i] = Some(v);
+            if wh.iter().all(Option::is_some) { break }
+          },
+          _ => { },
+        }
+      }
+      Some(PosC::try_from_iter_2(
+        wh.into_iter().map(|v| v.ok_or_else(|| SvSE::OneOfWidthHeight))
+      )?)
+    }
+
+    let _ = get_width_height(&svg_data).map_err(|error| SpE::SVGError {
+      error,
+      item_name: item_name.as_str().into(),
+      item_for_lib: lib_name_for.into(),
+      item_for_item: item_for.into(),
+    })?;
+
     Html::from_html_string(svg_data)
   }
 
index 73df6f59a8a32b5fcec6a233480942e984f766ff..f79f8efdd064743642b4eff5961d2b3549fa9908 100644 (file)
@@ -92,6 +92,12 @@ pub enum SpecError {
   CurrencyQtyNotMultipleOfUnit,
   #[error("coordinate overflow")]
   CoordinateOverflow(#[from] CoordinateOverflow),
+  #[error("SVG handling error: {item_name} for {item_for_lib} {item_for_item} {error}")] SVGError {
+    item_name: String,
+    item_for_lib: String,
+    item_for_item: String,
+    error: crate::shapelib::SVGSizeError, // TODO it needs to move
+  },
   #[error("image for supposedly-occultable piece \
            is not itself occultable but has multiple faces")]
   UnoccultableButRichImageForOccultation,