77665


長文大変失礼いたします．

## 背景

XMLで書かれたドキュメント群を変換してHTMLにし、CSSや画像ファイルとセットでパブリッシングするのをやっています．日本語だけでなく多言語をやっております．今般お客様の意向で、従来CSSで`@font-
face`で指定していたフォントファイルを、今はやりのWeb Fontに置き換えることになりました．

主な狙いは日本語フォントなんだろうと思います．サーバー側に置いたTrueTypeやOpenTypeのフォントを直接`@font-
face`でアサインしてしまうと、フォントファイルの大きさはそれなりにありますので、ダウンロードやハンドリングに時間がかかります．Web
Fontを使うと、HTMLに忍び込ませたJavaScriptで、そのWebページで使っている文字を調べて、動的にサーバー側から`.woff`でサブセット化して送ってくれるそうです．それならブラウザの負担も減るでしょう．（早いかどうかはいろいろ議論があるようです）

さて、Web Fontにすると、今までCSSの中で自分で`@font-face`を指定したものが、Web
Fontのサービスが自動的に生成してくれるCSSの`.woff`への`@font-face`を使うようになります．Web
Fontのサービスは、CSSの`font-family:～;`指定から、自動的に`@font-face`を自動生成してくれるようです．

ここまではいいことづくめだったのですが、問題が持ち上がりました．実はCSSの中には、`font-weight:
bold;`が当たり前に書かれています．ところがWeb
Fontの`.woff`を使用するようになると、ブラウザは自動的にボールドフェースをシミュレートしてくれなくなるのです．それまでTrueTypeのフォントファイルを`@font-
face`でアサインしていた時は、ブラウザがボールドフェースをシミュレート表示してくれていました．

この結果、`font-weight:
bold;`はすべて無視されちゃって、Webページはノーマルフェースだけで文字の表示という、実にのっぺらぽうなものになってしまいます．これはとても見ていられません．

これを解決するにはWeb Fontを使う限り、以下のようにしなければなりません．（英文フォントの例になります．ファミリー名は正確ではありません）

  * 大元を取り仕切るCSSで`font-family: FuturaPro-Light;`とやっていたら
  * ボールドにしたいときは、あちこちのCSSに存在する`font-weight: bold;`はダメで、例えば`font-family: FuturaPro-Book;`と書かねばならない．（これで太字に見えてくれます）

でもですね、フロントエンドのデザイナーさんが作ったCSSは、言語にほぼ全部共通で同じもを使っているのです．なので、運用側の都合で、ある言語ではWeb
Fontを使用、でもある言語ではクライアントにあるsans-serifでいいやというのは非常に困るのです．`font-weight:
bold;`くらいは、どのフォントを使用しようが、それを意識することなしに自由に記述したいものです．

ちなみに多言語ですが、`font-
family`の指定は大本のCSSの一ヶ所でやっているだけです．もちろんアラビア語なんかのためのLTR/RTLには対応しています．（以下で少々触れますSassで吸収しています．）

## 考えた方法

仕方がないのでWeb Fontを使用する言語のビルドでは、最終盤のステップでCSSの中の`font-weight:
bold;`があったら、そのメディアやセレクタを覚えておいて、最後に`font-family: FuturaPro-
Book;`と追記してやろうか？？と考えました．

ではこれをどうやってやるかを考えたのですが、パブリッシングのためのビルドで、プログラムでCSSを書き換えるってそう簡単ではないですよね．システムがJavaベースなのでJavaのCSSのパーサーを探しました、パーサーでCSSを読んで、それをXML(!)に変換してしまう、そのXMLを読んで上記の書き換え処理をXSLTで実装して、結果をまたCSSに書き戻すというものです．Webで良く出てくる
[CSS Parser](https://sourceforge.net/projects/cssparser/)
を試してみたんですが、次のようなCSSを食わせるとお亡くなりになってしまいました．

[code]

    @charset "UTF-8";
    body { direction: ltr; }
    body { font-family: "メイリオ",Meiryo,"ヒラギノ角ゴ Pro W3","Hiragino Kaku Gothic Pro",HiraKakuPro-W3,"ＭＳ Ｐゴシック","MS PGothic",Osaka,Verdana,sans-serif; color: #000; line-height: 1.5; background-color: #fff; overflow: hidden; }
    
[/code]

以下は試してみたプログラムの一部：

[code]

    public class Css2XmlImpl {
        private String mInputCssPath;
        private String mOutputXmlPath;
        
        /**
         * Constructor
         * @param inputCssPath
         * @param outputXmlPath
         * @throws IOException 
         */
        public Css2XmlImpl(String inputCssPath, String outputXmlPath) throws IOException {
            mInputCssPath = inputCssPath;
            mOutputXmlPath = outputXmlPath;
        }
        public void parse() throws IOException {
            InputStream cssStream = null;
            XmlWriter xmlOutput = null;
            try {
                xmlOutput = new XmlWriter(mOutputXmlPath);
                cssStream = new FileInputStream(mInputCssPath);
                InputSource source = new InputSource();
                source.setByteStream(cssStream);
                source.setEncoding("UTF-8");
                SACParserCSS3 parser = new SACParserCSS3();
                parser.setDocumentHandler(new Css2XMLHandler(xmlOutput));
                parser.parseStyleSheet(source);
            }
            catch (IOException ioe)
            {
                System.err.println ("IO Error: " + ioe);
            }
            catch (Exception e)
            {
                System.err.println ("Error: " + e);
    
            }
            finally
            {
                xmlOutput.close(); 
            }
        }
    }
    
    class Css2XMLHandler implements DocumentHandlerExt {
      // ここでSaxのようにCSSパーサーのいろんなイベントを拾ってXMLに書く．
    }
    
[/code]

こけたときの途中結果のXMLはこんな感じです．

[code]

    <?xml version="1.0" encoding="UTF-8"?>
    <css>
      <charset arg="UTF-8"/>
      <selector>
        <selector-item>body</selector-item>
        <property name="direction" LexicalUnit="ltr" important="false"/>
      </selector>
      <selector>
        <selector-item>body</selector-item>
        <property name="font-family" LexicalUnit="メイリオ" important="false"/>
      </selector>
    </css>
    
[/code]

どうも

[code]

    font-family: "メイリオ",Meiryo,"ヒラギノ角ゴ Pro W3","Hiragino Kaku Gothic Pro",HiraKakuPro-W3,"ＭＳ Ｐゴシック","MS PGothic",Osaka,Verdana,sans-serif;
    
[/code]

の`font-family`の記述がパージングできずに、その先おかしくなってしまうようです．結果は以下のように出ます．

[code]

    Error: org.w3c.css.sac.CSSException: unknown error
    
[/code]

確かにちょっと変則的な書き方ですが、これくらいのCSSがパージングできないようでは全然ダメですね．

## お伺いしたいこと

私が直面している問題は、Web
Fontを使用するなら、フォントには依存しますが、どのような方でも共通じゃないか？と思うのですが、いかがでしょう？実際どのように解決しておられるでしょうか？

## 追記

実は、パブリッシングのためのビルドで、Sassを使用しています．なので、`.scss`ファイルの`font-weight:
bold;`を、`@include bold-font`のようなmixinに置き換えて、そのmixinのbold-
fontをSassのコンパイル毎に動的に生成しようかという案もありました．

［動的に生成するmixin：Web Fontの場合］

[code]

    @mixin bold-font {
        font-family: FuturaPro-Book;
    }
    
[/code]

［動的に生成するmixin：Web Fontでない場合］

[code]

    @mixin bold-font {
        font-weight: bold;
    }
    
[/code]

でもよく考えるとCSSというフロントデザインを、Web
Fontを使うか/使わないか？という運用の問題でmixinに書き換えるというのも何か変な気がします．フロントエンド側はそこまで考えずに、デザインするのが筋ではないかと？なので、できたら、最終版でCSSを一気に書き換える方法をとりたいと考えております．

以上 よろしくお願いいたします．


CC BY-SA 4.0
2021-06-19T18:21:22.527
77661
2021-06-21T10:32:54.023
2021-06-20T02:43:21.940
9503
9503
question
java css sass
ビルドでCSSを書き換えるには？


`@font-face`は同名の`font-family`でweightの異なるフォントファイルを複数登録できます。  
例えば、weightの違う複数のwoff2ファイルを持つフォント "Foo Sans"があったとします。

[code]

    @font-face {
      font-family: 'Foo Sans';
      src: url('./assets/fonts/fooSans-Regular.woff2') format("woff2");
      font-weight: normal;
      font-style: normal;    
    }
    @font-face {
      font-family: 'Foo Sans';
      src: url('./assets/fonts/fooSans-Bold.woff2') format("woff2");
      font-weight: bold;
      font-style: normal;    
    }
    
[/code]

上記の様にフォントファイルを配置し、

[code]

    .foo{
      font-family: 'Foo Sans';
      font-size: 24pt;
      font-weight: normal;
    }
    .bold{
      font-weight: bold;
    }
    
[/code]

というスタイルを用意すれば、html側で

[code]

      <div>これは通常のフォントです。</div>
      <div class="foo">これは Foo Sans フォントです。</div>
      <div class="foo bold">これは Foo Sans フォントの bold です。</div>
    
[/code]

と記載すれば、3行目のdivは期待通り Foo Sans の太字で表示されます。

自前でwoffファイルを配信するのであれば上記の方法で問題なくfont-weightの指定ができる（しかも当該font-
weightの指定がないドキュメントであれば当該のフォントファイルはロードされない）と思われますが、例えばfontplusなどのサービスを使用している場合はscriptでフォントファイルとcssを生成しているので標準的な方法では難しいかもしれません。（エンタープライズプランであればこのようなカスタマイズに応じてもらえるかもしれません）  
あるいは、これもfontplus限定の話ですがwebフォントのロードが完了したらコールバック関数を仕掛けることができるようなので、そのコールバック関数でcssの中身を解析して回答本文のような`@font-
face`を生成する、という方法も考えられます。

### 追記

コメントと、私が投稿時に考えていたアイデアを具体的にご紹介します。

fontplusのフォントは仰る通りjavascriptでドキュメントの中身を走査し、実際に使用されているフォントをDOMツリーに組み込んでいるようです（その際組み込んだstyleタグには特定のidが付与されているようです）

[code]

    @font-face {
      font-family: 'FuturaPro-Light';
      font-weight: bold;
      src: url(FuturaPro-LightのURL);
    }
    
    @font-face {
      font-family: 'FuturaPro-Book';
      font-weight: bold;
      src: url(FuturaPro-BookのURL);
    }
    
[/code]

javascriptのAPIで公開されている、フォントがロード完了したら実行できるコールバック関数内で、上記style要素を探し、以下のようなcssを追加します。

[code]

    @font-face {
      font-family: 'FuturaPro';
      font-weight: normal;
      src: url(FuturaPro-LightのURL);
    }
    
    @font-face {
      font-family: 'FuturaPro';
      font-weight: bold;
      src: url(FuturaPro-Book);
    }
    
[/code]

font-familyは同名にし、weightを変え、src内のurlはスクリプトで追加されたurlを抽出して同じ値をセットします。  
このcssを追加すれば、ドキュメント内でベースのfont-familyを `font-family: 'FuturaPro'; font-weight:
normal;`とし、要所で`font-weight: bold;`が使えるのではないか、ということです。

ただし、javascriptでドキュメント内のfont-familyを走査しているのであれば実際に`FuturaPro-
Light`などのフォント指定が出てこなければなりません。そこで、ドキュメントベースのfont-familyを

[code]

    font-family: 'FuturaPro', 'FuturaPro-Light', 'FuturaPro-Book';
    
[/code]

というように指定すし（抽出対象が全文字になってしまいますが）、javascript側で代替フォント部分までフォローしてくれるのであれば指定したフォントを取得してないだろうか、ということろまでが私のアイデアです。

また、javascfriptのAPIには [フォント指定](https://fontplus.jp/usage/javascript-
api/method#api-8)というものがあり、これを使用すれば上記のfont-family指定に関係なく指定のフォントが取得できるのかもしれません。


CC BY-SA 4.0
2021-06-20T13:26:25.573
77665
2021-06-21T10:32:54.023
2021-06-21T10:32:54.023
46944
46944
77661
answer