方眼紙あります

電子工作のお供に 2.54mm(0.1 inch)の方眼紙を作りました。どうやって作ったかは後にしてとりあえず現物を置いておきます。クリックすると PDF が開きますので、欲しいものをダウンロードしてください。

印刷の際は、PDFビュワーの印刷設定で「原寸で印刷」を指定してください。印刷してみて 10マス 25.4mm あるいは 20マス 50.8mm になっていれば OK です。モノクロ印刷でよいですが、印刷画質は高めがよいと思います。

使い道

この方眼紙は、電子工作でプリント基板を作るときに使います。CADなんかを使えば必要のないものですが、ちょっとした回路など手書きで考えた方が手軽な場合に活用してください。
使い方は非常にアナログで、とりあえずこんな感じで鉛筆と消しゴムでパターンを設計します。

パターン設計

次に穴開け用に穴の位置を写し取ってプリント基板のサイズに合わせて切り抜きます。必要に応じてケースの穴開け用のものも作ります。

穴開け準備

基板穴開け用のものは基板にのりやテープで貼り付けて、穴開け位置に穴を開けていきます。穴は貫通させてもさせなくてもよいですが、両面基板の場合は貫通させておく方がよいと思います。

穴開け

そしたら穴を目印にパターンをマジックで手書きしていきます。はい、転写などという高度なことはできないので、地道に描いてください。それが済んだらエッチングして基板の完成ですが、方眼紙の使い方としてはここまでです。どうでしょう、自作してる感に溢れてるでしょう?
なお、設計はともかく基板に書く時にはベタを多くした方がエッチングも速く済みますしエッチング液も長持ちします。

パターン手書き

ちなみにパターンを書くのには青マッキーがいいです。レジストペンとかそこらで買えませんし、感光基板に使うわけでもありませんし、お安いですし。インクもこってり乗って使用感の上でもとても良く、次善の代用品というよりもむしろ第一選択肢です。黒マッキーじゃだめなのかはよく分かりません。なぜか青マッキーが人気のようです。以前メーカーに青マッキーが他色に比べて違う点がありそうか問い合わせてみたところでは、このような使い方においての知見が無いので分かりかねますとのことでした。でもきれいですよね、青マッキー。

青マッキー

PDFの作り方

PDFファイルって、その気になれば手書きできるんですよ。知ってました?全てテキストエディタだけで記述できるくらいです。実際はバイト位置なんかを書かないといけなかったりで手書きはきついのですが、不可能ではないです。

仕様書の原本が見られなくて確認できませんが、後述の xref を省略できるならバイト位置記述の必要は無くなるので普通に手書きできます。実際 xref(と startxref)を省略しても PDF として開けるっぽいです。

とまあ生成は簡単そうなので、方眼紙もこんな感じで PHPスクリプトで生成しました。自分好みの方眼紙を作りたいなら参考になるかもしれません。

<?php
// 電子工作方眼紙生成

// ページサイズ A4 (1/72インチ単位)
$pgw = floor(210 * 7200 / 25.4) / 100;
$pgh = floor(297 * 7200 / 25.4) / 100;
$spcw = floor(15 * 7200 / 25.4) / 100;
$spch = floor(20 * 7200 / 25.4) / 100;

// 罫線領域
$gu = 0.1 * 72; // 間隔 0.1 inch (2.54 mm)
$gnx = (int)(($pgw - $spcw * 2) / $gu); // 垂直罫線数
$gny = (int)(($pgh - $spch * 2) / $gu); // 水平罫線数
$gx0 = ($pgw - $gu * $gnx) / 2; // 原点X
$gy0 = ($pgh - $gu * $gny) / 2; // 原点Y
$gx1 = $gx0 + $gu * $gnx;
$gy1 = $gy0 + $gu * $gny;
$gw1 = 0.2;  // 細
$gw2 = 0.35; // 中
$gw3 = 0.5;  // 太
$gwd = $gw2; // ドット
$gc = 0.4; // 罫線の濃さ


$draw = [];
$draw[] = "/DeviceGray CS";

// 外枠
$draw[] = "${gw3} w";
$sw = $gw3;
$draw[] = "0.0 SC";
{
    $w = $gu * $gnx;
    $h = $gu * $gny;
    $draw[] = "${gx0} ${gy0} ${w} ${h} re S";
}
// テキスト
{
    $x = $gx1 - 32;
    $y = $gy1 + 2;
    $draw[] = "/FONT1 8 Tf";
    $draw[] = "BT ${x} ${y} Td (2.54 mm) Tj ET";
}

$draw[] = "${gc} SC";

$isEmpty = true;

// 垂直罫線
for ($i = 1; $i < $gnx; $i++) {
    if ($i % 10 == 0 || $i == $gnx) {
        $w = $gw3;
    } elseif($i % 5 == 0) {
        $w = $gw2;
    } else {
        $w = $gw1;
    }
    if ($w != $sw) {
        if (!$isEmpty) {
            $draw[] = "S";
            $isEmpty = true;
        }
        $draw[] = "${w} w";
        $sw = $w;
    }

    $x = $gx0 + $i * $gu;
    $draw[] = "${x} ${gy0} m ${x} ${gy1} l";
    $isEmpty = false;
}

// 水平線
for ($i = 1; $i < $gny; $i++) {
    if ($i % 10 == 0 || $i == $gny) {
        $w = $gw3;
    } elseif($i % 5 == 0) {
        $w = $gw2;
    } else {
        $w = $gw1;
    }
    if ($w != $sw) {
        if (!$isEmpty) {
            $draw[] = "S";
            $isEmpty = true;
        }
        $draw[] = "${w} w";
        $sw = $w;
    }
    $y = $gy0 + ($gny - $i) * $gu;
    $draw[] = "${gx0} ${y} m ${gx1} ${y} l";
    $isEmpty = false;
}

$draw[] = "S";


// 0.5目盛り無し版出力
createPaper('ElecGridPaperA.pdf', $pgw, $pgh, $draw);


// 点線 0.5目盛りあり版
{
    $draw2 = $draw;

    // 0.5部分に点線を引く
    $d0 = $gu / 4;
    $d1 = $d0;
    $dp = $gu / 8;

    $draw2[] = "${gw1} w";
    $draw2[] = "[ ${d0} ${d1} ] ${dp} d";
    for ($i = .5; $i < $gnx; $i++) {
        $x = $gx0 + $i * $gu;
        $draw2[] = "${x} ${gy0} m ${x} ${gy1} l";
    }
    for ($i = .5; $i < $gny; $i++) {
        $y = $gy0 + $i * $gu;
        $draw2[] = "${gx0} ${y} m ${gx1} ${y} l";
    }
    $draw2[] = "S";

    createPaper('ElecGridPaperB.pdf', $pgw, $pgh, $draw2);
}


// ドット 0.5目盛りあり版
{
    $draw2 = $draw;

    // 0.5部分に白線で隙間を空ける
    $draw2[] = "1.0 SC";
    $draw2[] = "${gw3} w";
    for ($i = .5; $i < $gnx; $i++) {
        $x = $gx0 + $i * $gu;
        $draw2[] = "${x} ${gy0} m ${x} ${gy1} l";
    }
    for ($i = .5; $i < $gny; $i++) {
        $y = $gy0 + $i * $gu;
        $draw2[] = "${gx0} ${y} m ${gx1} ${y} l";
    }
    $draw2[] = "S";

    // 0.5部分にドットを打つ
    $d0 = $gwd;
    $d1 = $gu - $d0;
    $dp = ($gu + $d0) / 2;

    $draw2[] = "${gc} SC";
    $draw2[] = "${d0} w";
    $draw2[] = "[ ${d0} ${d1} ] ${dp} d";
    for ($i = .5; $i < $gnx; $i++) {
        $x = $gx0 + $i * $gu;
        $draw2[] = "${x} ${gy0} m ${x} ${gy1} l";
    }
    $draw2[] = "S";

    createPaper('ElecGridPaperC.pdf', $pgw, $pgh, $draw2);
}



// PDF生成
function createPaper($fn, $pgw, $pgh, $draw)
{
    $pdf = [];
    $pdf[0] = '%PDF-1.2';

    // カタログ辞書
    $pdf[1] = '/Type /Catalog /Pages 2 0 R';

    // ページツリーノード
    $pdf[2] = "/Type /Pages /Kids [ 3 0 R ] /MediaBox [ 0 0 ${pgw} ${pgh} ] /Count 1";

    // ページオブジェクト
    $pdf[3] = '/Type /Page /Parent 2 0 R /Resources 4 0 R /Contents 6 0 R';

    // リソース
    $pdf[4] = '/Font << /FONT1 5 0 R >>';

    // フォント
    $pdf[5] = '/Type /Font /Subtype /Type1 /BaseFont /Helvetica';

    // コンテンツストリーム
    $pdf[6] = ['/Filter /FlateDecode', gzcompress(join(' ', $draw))];

    outPdf($fn, $pdf);
}


// PDF出力
function outPdf($fn, $pdf)
{
    $p = 0;
    $xref = [];
    $fp = fopen($fn, 'w');

    // header/body
    foreach ($pdf as $i => $obj) {
        if ($i == 0) {
            // header
            $buf = "$obj\n";
            $xref[$i] = "0000000000 65535 f \n";
        } else {
            // body
            if (is_array($obj)) {
                // with stream
                $size = strlen($obj[1]);
                $buf = "$i 0 obj\n" .
                    "<< $obj[0] /Length $size >>\n" .
                    "stream\n".
                    "$obj[1]\n".
                    "endstream\n".
                    "endobj\n";
            } else {
                // no stream
                $buf = "$i 0 obj\n" .
                    "<< $obj >>\n" .
                    "endobj\n";
            }
            $xref[$i] = sprintf("%010d %05d n \n", $p, 0);
        }
        fwrite($fp, $buf);
        $p += strlen($buf);
    }

    // xref
    {
        $ids = array_keys($xref);
        sort($ids);

        $secs = [];
        $s = -1;
        $chk = -1;
        foreach ($ids as $i) {
            if ($i != $chk) {
                $chk = $s = $i;
                $secs[$s] = [];
            }
            $secs[$s][] = $xref[$i];
            $chk++;
        }
        $buf = "xref\n";
        foreach ($secs as $s => $sec) {
            $cnt = count($sec);
            $buf .= "$s $cnt\n" . join('', $sec);
        }
        fwrite($fp, $buf);
    }

    // trailer
    {
        $size = count($pdf);
        $buf = "trailer\n" .
            "<< /Size ${size} /Root 1 0 R >>\n" .
            "startxref\n" .
            "${p}\n" .
            "%%EOF\n";
        fwrite($fp, $buf);
    }

    fclose($fp);
}

素の PHP で書いてるので必要なライブラリなどはありません。
配列の初期化に $draw = []; のような書式を使っているので PHP5.4 以降で動作します。それ以前でも $draw = Array(); のように置き換えれば動かせると思います。

ざっくり説明

PDF の構造はこんな感じになってます。

PDF構造

カタログ辞書を頂点とした構造の下層にページオブジェクトがあって、それがPDFの各ページの定義になります。それが指し示すコンテンツストリームに、ページの描画内容が入っています。実線矢印はオブジェクト番号での参照、破線矢印はバイト位置での参照です。xref がオブジェクト番号とバイト位置の変換テーブルです。
方眼紙はこのコンテンツストリームに罫線描画を詰め込んでできています。

ページの座標系は左下が原点で、単位は 1/72インチです。値 72 で 1インチ(25.4mm)で、小数で指定できます。PDFファイル内の寸法的なものは基本的に 1/72インチ単位ということに留意してください。

ページ座標系

今回のページの描画内容についてかいつまんで説明すると、まずは次の記述で白黒スケールにしています。

/DeviceGray CS

CS はストローク(線の描画など)の色空間の指定です。
あとはひたすらこんな感じで線を引きます。

x0 y0 m
x1 y1 l
S

m は描画位置を指定座標に移動します。l は描画内容として新たな座標までの直線を追加します。S は描画内容を描画します。とどのつまり、直線の始点を m で、終点を l で指定して、S で線を引いています。同じ色や太さの描画内容をたくさんため込んで、ひとつの S でまとめて描画することもできます。
この際に引く線の色や太さは、S を行う前に次のように指定しておきます。

太さ w
 SC

w は線の太さを 1/72インチ単位で、SC はストロークの色を0.0(黒)~1.0(白) で指定します。
最後にもうひとつ、破線の指定をする d です。

破線の指定
[ 描画長 間隔 ] 描画開始位置 d

破線の描画する部分の長さと間隔を 1/72インチ単位で指定します。両者が同じ値なら 2つ目の数字を省略できます。描画開始位置では、破線 1周期の長さ 描画長+間隔 の範囲のどの位置から破線の描画を開始するのかを指定します。これで方眼紙B の 0.5目盛りや方眼紙C のドットを描いています。
破線にした後実線に切り替えるには、パラメータ無しでただ d とすればいいっぽいです(仕様上正しいか未確認なのですが)。

といった感じで、簡単ですが説明終わりです。スクリプトの見通しが良くなりましたか?
描画コマンドの区切りは空白でも改行でもよいです。

ここで、次のように gzcompress を使っている部分を

    // コンテンツストリーム
    $pdf[6] = ['/Filter /FlateDecode', gzcompress(join(' ', $draw))];

こんな感じで圧縮せず出力するようにすると、PDF をテキストエディタで開いて方眼紙描画のコードが見られるので面白いかもしれません。

    // コンテンツストリーム
    $pdf[6] = ['', join(' ', $draw)];

せっかくなのでプレーンテキスト版も置いておきます。区切りを改行にするなどして見やすくしておきました。
ZIPを展開して出てくる PDF をテキストエディタで開けばどんな感じか分かります。もちろん PDFビュワーでも開けます。

参考リンク

PDF のフォーマットについてはこちらを大変参考にさせていただきました。ありがとうございました。

https://aznote.jakou.com/prog/pdf/index.html

このシリーズ

この記事です