feat: new articles
This commit is contained in:
153
source/_posts/2021/affinity-thumbnail.md
Normal file
153
source/_posts/2021/affinity-thumbnail.md
Normal file
@@ -0,0 +1,153 @@
|
||||
---
|
||||
title: Distill Thumbnail from .afphoto and .afdesign
|
||||
date: 2021-02-14T13:30:00
|
||||
---
|
||||
|
||||
Nextcloud does not have support for generating thumbnails from Affinity Photo and Affinity Design. Fine, I'll do it myself.
|
||||
|
||||
# Digging Binary
|
||||
|
||||
Glancing at `.afphoto` and `.afdesign` in Finder, I noticed that it has a QuickLook support and an ability to show the thumbnail image. So these files should have thumbnail image somewhere inside its binary.
|
||||
|
||||
I wrote a simple script to seek for thumbnail image inside a binary and save it as a PNG file.
|
||||
|
||||
```js
|
||||
const fs = require("fs");
|
||||
|
||||
// png spec: https://www.w3.org/TR/PNG/
|
||||
const PNG_SIG = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
|
||||
const IEND_SIG = Buffer.from([73, 69, 78, 68]);
|
||||
|
||||
function extractThumbnail(buf) {
|
||||
const start = buf.indexOf(PNG_SIG);
|
||||
const end = buf.indexOf(IEND_SIG, start) + IEND_SIG.length * 2; // IEND + CRC
|
||||
return buf.subarray(start, end);
|
||||
}
|
||||
|
||||
function generateThumbnail(input, output) {
|
||||
const buf = fs.readFileSync(input);
|
||||
const thumbBuf = extractThumbnail(buf);
|
||||
fs.writeFileSync(output, thumbBuf);
|
||||
}
|
||||
|
||||
generateThumbnail(process.argv[2], process.argv[3] || "output.png");
|
||||
```
|
||||
|
||||
That's right. This script just scrapes a binary file and distill a portion of which starts with `PNG` signature and ends with `IEND`.
|
||||
|
||||
Now I can generate a thumbnail image from arbitrary `.afphoto` and `.afdesign` file. Let's move on delving into Nextcloud source code.
|
||||
|
||||
# Tweaking Nextcloud
|
||||
|
||||
I have a little experience in tweaking Nextcloud source code before, where I implemented thumbnail generator for PDFs, so it should be easier this time, hopefully.
|
||||
|
||||
Long story short, I got Nextcloud generates thumbnail images for Affinity files by implementing `ProviderV2` class.
|
||||
|
||||
```php lib/private/Preview/Affinity.php
|
||||
<?php
|
||||
|
||||
namespace OC\Preview;
|
||||
|
||||
use OCP\Files\File;
|
||||
use OCP\IImage;
|
||||
use OCP\ILogger;
|
||||
|
||||
class Affinity extends ProviderV2 {
|
||||
public function getMimeType(): string {
|
||||
return '/application\/x-affinity-(?:photo|design)/';
|
||||
}
|
||||
|
||||
public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
|
||||
$tmpPath = $this->getLocalFile($file);
|
||||
|
||||
$handle = fopen($tmpPath, 'rb');
|
||||
$fsize = filesize($tmpPath);
|
||||
$contents = fread($handle, $fsize);
|
||||
$start = strrpos($contents, "\x89PNG");
|
||||
$end = strrpos($contents, "IEND", $start);
|
||||
$subarr = substr($contents, $start, $end - $start + 8 );
|
||||
|
||||
fclose($handle);
|
||||
$this->cleanTmpFiles();
|
||||
|
||||
$image = new \OC_Image();
|
||||
$image->loadFromData($subarr);
|
||||
$image->scaleDownToFit($maxX, $maxY);
|
||||
|
||||
return $image->valid() ? $image : null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```patch lib/private/PreviewManager.php
|
||||
@@ -363,6 +365,8 @@
|
||||
$this->registerCoreProvider(Preview\Krita::class, '/application\/x-krita/');
|
||||
$this->registerCoreProvider(Preview\MP3::class, '/audio\/mpeg/');
|
||||
$this->registerCoreProvider(Preview\OpenDocument::class, '/application\/vnd.oasis.opendocument.*/');
|
||||
+ $this->registerCoreProvider(Preview\Affinity::class, '/application\/x-affinity-(?:photo|design)/');
|
||||
|
||||
// SVG, Office and Bitmap require imagick
|
||||
if (extension_loaded('imagick')) {
|
||||
```
|
||||
|
||||
```patch lib/composer/composer/autoload_static.php
|
||||
@@ -1226,6 +1226,7 @@
|
||||
'OC\\OCS\\Result' => __DIR__ . '/../../..' . '/lib/private/OCS/Result.php',
|
||||
'OC\\PreviewManager' => __DIR__ . '/../../..' . '/lib/private/PreviewManager.php',
|
||||
'OC\\PreviewNotAvailableException' => __DIR__ . '/../../..' . '/lib/private/PreviewNotAvailableException.php',
|
||||
+ 'OC\\Preview\\Affinity' => __DIR__ . '/../../..' . '/lib/private/Preview/Affinity.php',
|
||||
'OC\\Preview\\BMP' => __DIR__ . '/../../..' . '/lib/private/Preview/BMP.php',
|
||||
'OC\\Preview\\BackgroundCleanupJob' => __DIR__ . '/../../..' . '/lib/private/Preview/BackgroundCleanupJob.php',
|
||||
'OC\\Preview\\Bitmap' => __DIR__ . '/../../..' . '/lib/private/Preview/Bitmap.php',
|
||||
```
|
||||
|
||||
```patch lib/composer/composer/autoload_classmap.php
|
||||
@@ -1197,6 +1197,7 @@
|
||||
'OC\\OCS\\Result' => $baseDir . '/lib/private/OCS/Result.php',
|
||||
'OC\\PreviewManager' => $baseDir . '/lib/private/PreviewManager.php',
|
||||
'OC\\PreviewNotAvailableException' => $baseDir . '/lib/private/PreviewNotAvailableException.php',
|
||||
+ 'OC\\Preview\\Affinity' => $baseDir . '/lib/private/Preview/Affinity.php',
|
||||
'OC\\Preview\\BMP' => $baseDir . '/lib/private/Preview/BMP.php',
|
||||
'OC\\Preview\\BackgroundCleanupJob' => $baseDir . '/lib/private/Preview/BackgroundCleanupJob.php',
|
||||
'OC\\Preview\\Bitmap' => $baseDir . '/lib/private/Preview/Bitmap.php',
|
||||
```
|
||||
|
||||

|
||||
|
||||
It works!
|
||||
|
||||
# Bonus: PDF thumbnail generator
|
||||
|
||||
```php lib/private/Preview/PDF.php
|
||||
<?php
|
||||
|
||||
namespace OC\Preview;
|
||||
|
||||
use OCP\Files\File;
|
||||
use OCP\IImage;
|
||||
|
||||
class PDF extends ProviderV2 {
|
||||
public function getMimeType(): string {
|
||||
return '/application\/pdf/';
|
||||
}
|
||||
|
||||
public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage {
|
||||
$tmpPath = $this->getLocalFile($file);
|
||||
$outputPath = \OC::$server->getTempManager()->getTemporaryFile();
|
||||
|
||||
$gsBin = \OC_Helper::findBinaryPath('gs');
|
||||
$cmd = $gsBin . " -o " . escapeshellarg($outputPath) . " -sDEVICE=jpeg -sPAPERSIZE=a4 -dLastPage=1 -dPDFFitPage -dJPEGQ=90 -r144 " . escapeshellarg($tmpPath);
|
||||
shell_exec($cmd);
|
||||
|
||||
$this->cleanTmpFiles();
|
||||
|
||||
$image = new \OC_Image();
|
||||
$image->loadFromFile($outputPath);
|
||||
$image->scaleDownToFit($maxX, $maxY);
|
||||
|
||||
unlink($outputPath);
|
||||
|
||||
return $image->valid() ? $image : null;
|
||||
}
|
||||
}
|
||||
```
|
BIN
source/_posts/2021/affinity-thumbnail/afphoto.png
Normal file
BIN
source/_posts/2021/affinity-thumbnail/afphoto.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 437 KiB |
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: 点字の表現力
|
||||
date: 2021-02-13
|
||||
date: 2021-02-13T01:00:00
|
||||
---
|
||||
|
||||
「n 種類の文字を表現できる点字を作らないといけなくなったらどうしよう」
|
||||
@@ -11,7 +11,7 @@ date: 2021-02-13
|
||||
- $f(N,K) = \sum_{i=K → 0} N P K$
|
||||
- 2 の n 乗っぽい
|
||||
- べき集合の濃度?
|
||||
- 2 進数のブール配列と考えるとわかりやすくなった
|
||||
- 2 進数のブール配列と考えればわかりやすくなった
|
||||
- ということは X 個の表現をするために必要なブール配列の長さは $\lceil\log_2 (X)\rceil$
|
||||
- 例えばアルファベットなら 6 ドットの点字で表現できる
|
||||
- だから英語の点字は 6 つの点
|
||||
|
@@ -1,9 +1,9 @@
|
||||
---
|
||||
title: Arch Linux Setup Guide
|
||||
title: Installing Arch Linux
|
||||
date: 2021-02-12
|
||||
---
|
||||
|
||||
This note includes all commands I typed when I setup Arch Linux on my new baremetal server.
|
||||
This note includes all commands I typed when I set up Arch Linux on my new bare metal server.
|
||||
|
||||
# Why I choose Arch Linux
|
||||
|
||||
@@ -26,7 +26,7 @@ This note includes all commands I typed when I setup Arch Linux on my new bareme
|
||||
wipefs -a /dev/sda
|
||||
```
|
||||
|
||||
## create parition
|
||||
## create partition
|
||||
|
||||
```bash
|
||||
parted
|
||||
@@ -54,7 +54,7 @@ mount /dev/sda2 /mnt
|
||||
mount /dev/sda1 /mnt/boot
|
||||
```
|
||||
|
||||
## install base & linux kernel
|
||||
## install base & Linux kernel
|
||||
|
||||
```bash
|
||||
reflector -f 10 --latest 30 --protocol https --sort rate --save /etc/pacman.d/mirrorlist # optimize mirror list
|
||||
@@ -83,7 +83,7 @@ grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=GRUB
|
||||
grub-mkconfig -o /boot/grub/grub.cfg
|
||||
```
|
||||
|
||||
## ntp
|
||||
## NTP
|
||||
|
||||
```bash
|
||||
sed -i -e 's/#NTP=/NTP=0.arch.pool.ntp.org 1.arch.pool.ntp.org 2.arch.pool.ntp.org 3.arch.pool.ntp.org/' -e 's/#Fall/Fall/' /etc/systemd/timesyncd.conf
|
||||
@@ -252,7 +252,7 @@ reboot
|
||||
|
||||
# Additional setup
|
||||
|
||||
## gpgpu
|
||||
## GPGPU
|
||||
|
||||
```bash
|
||||
pacman -S nvidia
|
||||
@@ -297,7 +297,7 @@ usermod -aG docker user
|
||||
docker run --rm -it --gpus all nvidia/cuda:10.2-cudnn7-runtime
|
||||
```
|
||||
|
||||
## telegraf
|
||||
## Telegraf
|
||||
|
||||
```bash
|
||||
yay -S telegraf
|
||||
@@ -486,7 +486,7 @@ ln -sf /etc/backups/borg.* /etc/systemd/system/
|
||||
systemctl enable --now borg
|
||||
```
|
||||
|
||||
## kubernetes
|
||||
## Kubernetes
|
||||
|
||||
```bash
|
||||
pacman -S kubeadm kubelet kubectl
|
@@ -99,4 +99,4 @@ JWT は JSON を利用して Assertion を生成するための仕様。
|
||||
|
||||
2019 年 7 月
|
||||
|
||||
リソースサーバーに渡す Access Token に JWT を使用することを定めている。
|
||||
リソースサーバーへ渡す Access Token に JWT を使用することを定めている。
|
||||
|
26
source/_posts/2021/parseint-magic.md
Normal file
26
source/_posts/2021/parseint-magic.md
Normal file
@@ -0,0 +1,26 @@
|
||||
---
|
||||
title: "[].map(parseInt)"
|
||||
date: 2021-02-14T11:30:00
|
||||
---
|
||||
|
||||
Fun fact: `[0xa, 0xa, 0xa].map(parseInt)` yields `[10, NaN, 2]`.
|
||||
|
||||
# Why
|
||||
|
||||
```js
|
||||
parseInt(0xa, 0, [0xa, 0xa, 0xa]);
|
||||
```
|
||||
|
||||
The second argument is `0` so the first argument going to be treated as decimal number becoming `10`.
|
||||
|
||||
```js
|
||||
parseInt(0xa, 1, [0xa, 0xa, 0xa]);
|
||||
```
|
||||
|
||||
The second argument is `1` which is invalid as a radix, so the result ends up with `NaN`.
|
||||
|
||||
```js
|
||||
parseInt(0xa, 2, [0xa, 0xa, 0xa]);
|
||||
```
|
||||
|
||||
The second argument is `2` meaning the first argument going to be handled as a binary number. `0xa` is `10` in binary, which results in `2` in decimal form.
|
38
source/_posts/2021/server-2020.md
Normal file
38
source/_posts/2021/server-2020.md
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
title: 新しい自宅サーバーの構成
|
||||
date: 2021-02-13T00:00:00
|
||||
---
|
||||
|
||||
10 年ぶりにサーバーを更新しました。初めての AMD、初めての DDR4、初めての NVM Express です。
|
||||
|
||||
# 用途とスペック
|
||||
|
||||
- セルフホスト (Docker)
|
||||
- メールサーバー
|
||||
- DNS サーバー
|
||||
- Nextcloud
|
||||
- TimeMachine
|
||||
- GitLab
|
||||
- LanguageTool
|
||||
- VPN 他
|
||||
- 計算実験
|
||||
- Web サーバー
|
||||
- VS Code Remote SSH のホスト先
|
||||
|
||||
重いタスクを並列してやらせたいので最優先は CPU とメモリです。メモリはデュアルリンクを重視して 32GBx2 を、CPU は昨今のライブラリのマルチコア対応を勘案して Ryzen 9 3950X にしました。
|
||||
|
||||
> 結果から言うとメモリはもっと必要でした。巨大な Pandas データフレームを並列処理なんかするとサクッと消えてしまいます。予算に余裕があるなら 128GB ほど用意したほうが良いです。
|
||||
|
||||
GPU は古いサーバーに突っ込んでいた NVIDIA GeForce GTX TITAN X (Maxwell)を流用しました。グラフィックメモリが 12GB ですが、最大ワークロード時でも 5GB は残るので今のところ十分です。
|
||||
|
||||
記憶装置は 3TB HDD 2 台と 500GB NVMe、そして古いサーバーから引っこ抜いた 500GB SSD です。NVMe メモリは OS 用、SSD/HDD はデータとバックアップ用にしました。
|
||||
|
||||
マザーボードは X570 と比較して、実装されているコンデンサーやパーツがサーバー向きだと思った ASRock B550 Taichi にしました。
|
||||
|
||||
電源は、今後 GPU を追加することを考えて 800W 電源を選びました。実際にサーバーを稼働させながら使用電力を計測したところ、アイドル時に 180W 前後、フル稼働時でも 350W を超えない程度でした。今後 UPS を買う場合はその付近+バッファのグレードを買うと良いかもしれません。
|
||||
|
||||
ケースは Fractal Design の Meshify 2 です。
|
||||
|
||||
OS は長年付き合ってきた Ubuntu と袂を分かち、Arch Linux を選びました。ミニマルなところが好きです。本当に何も用意してくれません。セットアップウィザードとかないです。`which`すらインストールしなければ使えません。
|
||||
|
||||
Arch Linux のセットアップは[個別に記事](https://uechi.io/blog/installing-arch-linux/)を書いたので読んでください。入力したコマンドを全て記録しました。
|
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: 最小送金回数で精算する割り勘アルゴリズム
|
||||
date: 2021-02-14
|
||||
date: 2021-02-14T00:00:00
|
||||
---
|
||||
|
||||
大人数でキャンプを楽しんだあとに待っているのは耐え難き送金処理です。
|
||||
次回から楽をするために、送金回数を最小化する制約で精算表を作る方法を考えてみます。
|
||||
次回から楽をするためにも、送金回数を最小化する制約で精算表を作る方法を考えましょう。
|
||||
|
||||
# tl;dr
|
||||
|
||||
@@ -145,4 +145,4 @@ B virtually paid ¥81 in total
|
||||
C virtually paid ¥76 in total
|
||||
```
|
||||
|
||||
プログラムに落とし込めたら、あとはアプリを作るなりスプレッドシートのマクロにするなり自由です。面倒なことは全部コンピューターにやらせよう!
|
||||
プログラムに落とし込むことができたら、あとはスプレッドシートのマクロにするなり自由です。面倒なことは全部コンピューターにやらせよう。
|
||||
|
Reference in New Issue
Block a user