CSS調整

2017年12月10日

batikでsvgをpng化

昔はsvgなんてありえへん、ベクター画像は印刷用!eps!eps!って感じだったけれど、最近の自分はsvg無しでは生きていけなくなってしまいました
特にインラインのsvg
Angularみたいなどんな要素でもコンポーネント化できる環境だと(最終的なソースのでかさ以外は)何も気にせずsvgの内部をTSでいじり倒せるし
(canvasでええやんってのは置いておきます)

batik自体はまあ、今更感はある気がしますが、試してみます

まずは依存関係 pom.xml

<dependency>
    <groupId>org.apache.xmlgraphics</groupId>
    <artifactId>batik-transcoder</artifactId>
    <version>1.9.1</version>
</dependency>
<dependency>
    <groupId>org.apache.xmlgraphics</groupId>
    <artifactId>batik-codec</artifactId>
    <version>1.9.1</version>
</dependency>

svgの文字列を読み込む場合

val svgStr = ...
val doc = SAXSVGDocumentFactory(XMLResourceDescriptor.getXMLParserClassName())
   .createDocument(SVGDOMImplementation.SVG_NAMESPACE_URI, StringReader(svgStr))


createDocumentの第2引数がReaderやInputStreamを読み込めるので、

svgのファイルを読み込む場合

val doc = File("image.svg").inputStream().use {
 SAXSVGDocumentFactory(XMLResourceDescriptor.getXMLParserClassName())
  .createDocument(SVGDOMImplementation.SVG_NAMESPACE_URI, it)
}


生成されるのはorg.w3c.dom.Documentなので、

DOM操作は容易かと

val svgStr = ...
val doc = SAXSVGDocumentFactory(XMLResourceDescriptor.getXMLParserClassName())
 .createDocument(SVGDOMImplementation.SVG_NAMESPACE_URI, StringReader(svgStr)).apply {
 getElementById("title").textContent = "タイトル"
}


読み込んだsvgをネストしたい場合

はcloneして追加して適用する
val svgStr = ...
val doc = SAXSVGDocumentFactory(XMLResourceDescriptor.getXMLParserClassName())
 .createDocument(SVGDOMImplementation.SVG_NAMESPACE_URI, StringReader(svgStr)).apply {
 
 val svg2Str = ...
 val clone = SAXSVGDocumentFactory(XMLResourceDescriptor.getXMLParserClassName())
  .createDocument(SVGDOMImplementation.SVG_NAMESPACE_URI, StringReader(svg2Str))
  .documentElement
  .cloneNode(true)
 adoptNode(clone)
 documentElement.appendChild(clone)
}


pngにはき出すには

TranscoderInputとTranscoderOutputをPNGTranscoderに与えます
例ではTranscodeする際に、画像サイズを指定
val transOut = ByteArrayOutputStream()
PNGTranscoder().apply {
 val rect = Rectangle(0, 0, 500, 500)
 addTranscodingHint(PNGTranscoder.KEY_WIDTH, rect.width.toFloat())
 addTranscodingHint(PNGTranscoder.KEY_HEIGHT, rect.height.toFloat())
 transcode(TranscoderInput(doc), TranscoderOutput(transOut))
}


TranscoderOutputはWriterやOutputStreamを受け取るので、

ファイル出力するなら以下のような感じ

File("image.png").outputStream().use {
 PNGTranscoder().apply {
  val rect = Rectangle(0, 0, 500, 500)
  addTranscodingHint(PNGTranscoder.KEY_WIDTH, rect.width.toFloat())
  addTranscodingHint(PNGTranscoder.KEY_HEIGHT, rect.height.toFloat())
  transcode(TranscoderInput(doc), TranscoderOutput(it))
 }
}


ちなみに

Transcodeではなく出来上がったsvgをStringでほしいなら

batikは関係なくて、普通にjavax.xml周りを使って以下とか
val xmlStr = StringWriter().use {
 TransformerFactory.newInstance().newTransformer().apply { 
  setOutputProperty(OutputKeys.INDENT, "yes")
  setOutputProperty(OutputKeys.METHOD, "xml")
  setOutputProperty("{http://xml.apache.org/xalan}indent-amount", "2")
  transform(DOMSource(doc), StreamResult(it))
 }
 it.toString()
}


便利な世の中ですねぇ~

いじょ

2017年8月20日

スクロールの値

scrollTop、clientHeight、scrollHeight・・・よくゴッチャになる


毎回使うたびに忘れてるのでメモ

scrollTop
要素内のスクロール量
clientHeight
スクロール要素の高さ
scrollHeight
スクロール要素内の高さ
例:
 body(画面全体)でoverflowしている場合、
 ブラウザ表示領域がclientHeight(例として: 1000px)
 overflowによりhidden状態の要素を含む高さがscrollHeight(例として: 10000px)
 今のスクロール量がscrollTop(上の例で考えた場合、画面2つ分程度スクロールしていたら、2000px)
 上の例の場合のスクロール率は2000px/10000pxで20%

2017年6月2日

ll が無くて困ったとき

たまーにdebianとかで

llって打ってコマンド無いよ

って言われる時用。よく忘れるのでメモ

llはls -lとほぼほぼ同じなのでエイリアスを作っとくだけでいい
alias ll='ls -l --color=auto'

2017年3月23日

Spring Data Restで@Projection

まず、Spring Data RestでサクッとRestできるようにします

DB

適当クエリでテーブル2つとデータを作成
create table memo_title (
  id serial primary key,
  title varchar(255) not null,
  comment varchar(255)
);

create table memo_value (
  id serial primary key,
  value varchar(255) not null,
  title_id int not null
);

insert into memo_title(id, title, comment) values(1, 'TESTタイトル', 'メモのコメントですよ');
insert into memo_value(id, value, title_id) values(1, 'ほげ', 1);
insert into memo_value(id, value, title_id) values(2, 'ふが', 1);

memo_value が memo_title の id を持ってて親子関係がある感じです。
(面倒なのでFKとか無しで)



続いてAPのコード

MemoTitle.kt

@Entity
@Table(name="memo_title")
class MemoTitle {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    var id: Long = 0

    @Column(nullable = false)
    lateinit var title: String

    @Column
    lateinit var comment: String
}

@Repository
interface MemoTitleRepository: PagingAndSortingRepository<MemoTitle, Long>

MemoValue.kt

@Entity
@Table(name="memo_value")
class MemoValue {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    var id: Long = 0

    @Column(nullable = false)
    lateinit var value: String

    @Column(nullable = false)
    var title_id: Long = 0
}

@Repository
interface MemoValueRepository : PagingAndSortingRepository<MemoValue, Long>

こういう時に kotlin だと関連したコードが1ファイルに書けるってだけでウレシイ。。。



この時点で実行すると

MemoTitle

 
curl http://localhost:9000/api/memoTitles

{
  "_embedded" : {
    "memoTitles" : [ {
      "title" : "TESTタイトル",
      "comment": "メモのコメントですよ",
      "_links" : {
        "self" : {
          "href" : "http://localhost:9000/api/memoTitles/1"
        },
        "memoTitle" : {
          "href" : "http://localhost:9000/api/memoTitles/1"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:9000/api/memoTitles"
    },
    "profile" : {
      "href" : "http://localhost:9000/api/profile/memoTitles"
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 1,
    "totalPages" : 1,
    "number" : 0
  }
}

MemoValue

curl http://localhost:9000/api/memoValues

{
  "_embedded" : {
    "memoValues" : [ {
      "id" : 1,
      "value" : "ほげ",
      "title_id" : 1,
      "_links" : {
        "self" : {
          "href" : "http://localhost:9000/api/memoValues/1"
        },
        "memoValue" : {
          "href" : "http://localhost:9000/api/memoValues/1"
        },
        "title" : {
          "href" : "http://localhost:9000/api/memoValues/1/title"
        }
      }
    }, {
      "id" : 2,
      "value" : "ふが",
      "title_id" : 1,
      "_links" : {
        "self" : {
          "href" : "http://localhost:9000/api/memoValues/2"
        },
        "memoValue" : {
          "href" : "http://localhost:9000/api/memoValues/2"
        },
        "title" : {
          "href" : "http://localhost:9000/api/memoValues/2/title"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:9000/api/memoValues"
    },
    "profile" : {
      "href" : "http://localhost:9000/api/profile/memoValues"
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 2,
    "totalPages" : 1,
    "number" : 0
  }
}


MemoValueをGETしたときにtitleも欲しかったりしたとします
MemoValueにMemoTitleとのアソシエーションを追加し、Projectionを指定します

MemoValue.kt

@Entity
@Table(name="memo_value")
class MemoValue {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    var id: Long = 0

    @Column(nullable = false)
    lateinit var value: String

    @Column(nullable = false)
    var title_id: Long = 0

    @ManyToOne(targetEntity = MemoTitle::class, fetch = FetchType.EAGER)
    @JoinColumn(name = "title_id", insertable = false, updatable = false, nullable = false)
    lateinit var title: MemoTitle
}

@Projection(types = arrayOf(MemoValue::class))
interface MemoValueProjection {
    var id: Long
    var value: String
    var title: MemoTitle
}

@RepositoryRestResource(excerptProjection = MemoValueProjection::class)
interface MemoValueRepository : PagingAndSortingRepository<MemoValue, Long>

excerptProjectionとするとリポジトリの戻りが全てそのprojectionになります
ここで指定せずに、GETリクエストに?projection=Projection名とか指定することもできます


これで実行すると

MemoValue

curl http://localhost:9000/api/memoValues

{
  "_embedded" : {
    "memoValues" : [ {
      "id" : 1,
      "value" : "ほげ",
      "title" : {
        "title" : "TESTタイトル",
        "comment": "メモのコメントですよ"
      },
      "_links" : {
        "self" : {
          "href" : "http://localhost:9000/api/memoValues/1"
        },
        "memoValue" : {
          "href" : "http://localhost:9000/api/memoValues/1{?projection}",
          "templated" : true
        },
        "title" : {
          "href" : "http://localhost:9000/api/memoValues/1/title"
        }
      }
    }, {
      "id" : 2,
      "value" : "ふが",
      "title" : {
        "title" : "TESTタイトル",
        "comment": "メモのコメントですよ"
      },
      "_links" : {
        "self" : {
          "href" : "http://localhost:9000/api/memoValues/2"
        },
        "memoValue" : {
          "href" : "http://localhost:9000/api/memoValues/2{?projection}",
          "templated" : true
        },
        "title" : {
          "href" : "http://localhost:9000/api/memoValues/2/title"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:9000/api/memoValues"
    },
    "profile" : {
      "href" : "http://localhost:9000/api/profile/memoValues"
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 2,
    "totalPages" : 1,
    "number" : 0
  }
}

無事にアソシエーション先の情報も取れました



続いて、title.commentは要らないんすよ、とかなったとします
その場合はMemoTitleもProjectionを指定します

MemoTitle.kt

@Projection(types = arrayOf(MemoTitle::class))
interface MemoTitleProjection {
    var title: MemoTitle
}

MemoValue.kt

@Projection(types = arrayOf(MemoValue::class))
interface MemoValueProjection {
    var id: Long
    var value: String
    var title: MemoTitleProjection
}

これで実行すると

MemoValue

curl http://localhost:9000/api/memoValues

{
  "_embedded" : {
    "memoValues" : [ {
      "id" : 1,
      "value" : "ほげ",
      "title" : {
        "title" : "TESTタイトル"
      },
      "_links" : {
        "self" : {
          "href" : "http://localhost:9000/api/memoValues/1"
        },
        "memoValue" : {
          "href" : "http://localhost:9000/api/memoValues/1{?projection}",
          "templated" : true
        },
        "title" : {
          "href" : "http://localhost:9000/api/memoValues/1/title"
        }
      }
    }, {
      "id" : 2,
      "value" : "ふが",
      "title" : {
        "title" : "TESTタイトル"
      },
      "_links" : {
        "self" : {
          "href" : "http://localhost:9000/api/memoValues/2"
        },
        "memoValue" : {
          "href" : "http://localhost:9000/api/memoValues/2{?projection}",
          "templated" : true
        },
        "title" : {
          "href" : "http://localhost:9000/api/memoValues/2/title"
        }
      }
    } ]
  },
  "_links" : {
    "self" : {
      "href" : "http://localhost:9000/api/memoValues"
    },
    "profile" : {
      "href" : "http://localhost:9000/api/profile/memoValues"
    }
  },
  "page" : {
    "size" : 20,
    "totalElements" : 2,
    "totalPages" : 1,
    "number" : 0
  }
}

title.commentが削られています



これだけの記述で必要な情報のGETに加えてPOSTやらPUTやらPATCHでのINSERTやDELETEやUPDATEなんかもルーティングされてるとかステキですね

いじょ