0
$\begingroup$

How can I parse a .css file generated by Mathematica, for example when exporting a notebook to HTML? What is te best way to modify or add new values to the CSS?

I've written a simple function to add some values to a .css file:

ImportTxt[file_, Elements_: "String"] :=  Module[{file1 = file, absoluteFileName,    fileNameTxt}, (fileNameTxt = ((FileNameDrop[           absoluteFileName = AbsoluteFileName[#]] <> "\\" <>           FileBaseName[#]) &@file) <> ".txt");   If[FileExistsQ[fileNameTxt], DeleteFile[fileNameTxt];    CopyFile[file, fileNameTxt]; Import[fileNameTxt, Elements],    CopyFile[file, fileNameTxt]; Import[fileNameTxt, Elements]]]

ExportCSS[file_, content_] :=  Module[{fileTxt = file <> ".txt", fileCss = file <> ".css"},   Export[fileTxt, content];   If[FileExistsQ[fileCss], DeleteFile[fileCss];    RenameFile[fileTxt, fileCss], RenameFile[fileTxt, fileCss]]]

cssAdd[cssFile_, name_, value_] :=  Module[{cssFileTxt},   If[FileExistsQ[cssFile] == False, Print["File-Not-Exists"];    Abort[]]; cssData = ImportTxt[cssFile, "Data"];   cssDataList =    Partition[SplitBy[cssData, StringMatchQ[#, "/*" ~~ __ ~~ "*/"] &],     2];  pos = Position[cssDataList, "." <> name <> " {"][[1]];  result = Insert[cssDataList, value, pos + {0, 0, 1}];  ExportCSS[cssFile // StringDrop[#, -4] &,    StringJoin@Map[Riffle[#, "\n"] &, result, {1, 2}]]]

For example:

cssAdd["index", "Text", "width:300px;"]

will add the value width:300px; to the css file, but I encounter problems if I try to do this manipulation repeatedly.

If I continue, changing the value width:300px; into width:500px;, the second value doesn't work (ie. width:300px; will override the width:500px;).

In my code: I use pos + {0, 0, 1} to get the position where the new value will be inserted, and I think this could be part of my problem.

Let me try Reverse a sublist:

width:500px;

width:300px;

In context this looks like:

.Text{ 
width:500px;
width:300px;
padding:8px;
}

This is how I think it should look:

.Text{ 
width:300px;
width:500px;
padding:8px;
}

How can this approach be improved? For example, is it better to import as "String" or "Data"?

Since PageWidth doesn't seem to take effect after exporting to HTML, I need to manually map the value of PageWidth to the width in.css file.


Update one of my solution: I find the place of "}" after ".Text {"

pos1=Position[cssDataList,"."<>name<>" {"][[1]];
level=pos1[[1;;2]];
pos2=Select[Position[cssDataList,"}"][[All,-1]],#>pos1[[-1]]&]//First;
result=Insert[cssDataList,value,Append[level,pos2]];
$\endgroup$
6
  • $\begingroup$ @cormullion hi, I think you know much about something about .css file. $\endgroup$ Commented Dec 20, 2013 at 7:27
  • $\begingroup$ 500px will override 300px, yes, but isn't this what you want? $\endgroup$
    – C. E.
    Commented Dec 20, 2013 at 7:41
  • $\begingroup$ @Anon no, 300px override 500px, my function add a new value just after .name { $\endgroup$ Commented Dec 20, 2013 at 7:43
  • $\begingroup$ Hi - no I know very little about CSS, except that it cascades (inherits). But your question isn't clear - what are the different selectors for width. What is overriding what? And your 'parser' won't work on all CSS files (they can be very complex- even containing image data). I wonder why Mathematica doesn't parse CSS files? $\endgroup$
    – cormullion
    Commented Dec 20, 2013 at 8:42
  • $\begingroup$ @cormullion ah, fine, I mean the .css file generated by Mathematica Exporting notobook to HTML. $\endgroup$ Commented Dec 20, 2013 at 8:45

1 Answer 1

3
$\begingroup$

You've added some additional data since I started working on my solution and I now have a better understanding of your problem. You attempted to replace a value and ended up with

.Text{ 
width:500px;
width:300px;
padding:8px;
}

It's much easier to append the rule at the end of the file, so you get:

.Text{ 
width:500px;
padding:8px;
}

.Text{
width:300px;
}

This is valid CSS, where the last rule has precedence over the former. In fact, this property of CSS is very, very commonly used. For example almost every website includes a stylesheet that gets rid of browser inconsistencies. Then usually a sort of framework with handy and common rules, and then a third stylesheet with site-specific styles. Each stylesheet overrides the former. So what you ought to do is create a stylesheet that overrides the Mathematica styles you don't like and include it after you include the Mathematica stylesheet.

However, I've written a simple example of how you can work with CSS. It should work for the most common purposes. It only has one function so far, which is to add/update a property but you can easily extend it.

Let's start with some basic CSS:

css = ".navbar-inverse .navbar-brand,
  .navbar-inverse .navbar-nav > li > a {
    text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
  }

  .navbar-static-top,
  .navbar-fixed-top,
  .navbar-fixed-bottom {
    border-radius: 0;
  }

  .alert {
    text-shadow: 0 1px 0 rgba(255, 255, 255, 0.2);
    -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 \
1px 2px rgba(0, 0, 0, 0.05);
            box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.25), 0 \
1px 2px rgba(0, 0, 0, 0.05);
  }

  .alert-success {
    background-image: -webkit-linear-gradient(top, #dff0d8 0%, \
#c8e5bc 100%);
    background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc \
100%);
    background-repeat: repeat-x;
    border-color: #b2dba1;
    filter: \
progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', \
endColorstr='#ffc8e5bc', GradientType=0);
  }";

Here's the parser, I hope it's clear what it does from the comments. The regular expressions are very simple, one can think of them as string patterns where .*? is _.

{selectors, properties} = 
  Transpose@
   StringCases[StringReplace[css, "\n" -> ""](* The . 
    selector does not match newlines *),
    RegularExpression["(.*?){(.*?)}"] -> {"$1", 
          "$2"} (* $1 is the content of the first parenthesis, $2 that of \
the second *)
    ];
selectors = 
  StringTrim /@ 
   StringSplit[selectors, ","]; (* Selectors are divided by comma *)

properties = 
  StringTrim /@ 
     StringCases[#, 
      RegularExpression["(.*?):(.*?);"] -> {"$1", "$2"}] & /@ 
   properties; (* Each property is of the form name: value; *)

rules = Transpose[{selectors, properties}];

Now execute rules // TableForm and inspect the values. To change a value I created this utility function:

propertyUpdate[rules_, selector_, name_, value_] := If[
  MatchQ[rules, {___, {{___, 
      selector, ___}, {___, {name, ___}, ___}}, ___}](* 
  The property already has a value *),
  rules /. {x___, {s : {___, selector, ___}, {a : ___, {name, ___}, 
       b : ___}}, y___} :> {x, {s, {a, {name, value}, b}}, y},
  Append[rules, {selector, {{name, value}}}]
  ]

To try it, execute this and compare with the original value:

propertyUpdate[rules, ".alert", "box-shadow", "newValue"] // TableForm
$\endgroup$
1
  • $\begingroup$ ha, thanks, I need time to digest your answer. I see one important thing is we can just add a total .Text {width:300px;} in the end of the file, that broaden the functions we could write. I've added a solution along my function, find the place of "}". $\endgroup$ Commented Dec 20, 2013 at 9:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.