Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PHPでImageMagickとICUを使って画像に縦書きテキストを描画する / Draw vertical text in PHP using ImageMagick and ICU

2e72046813bf3dc01173fe54919a6d1d?s=47 cc4966
March 28, 2021

PHPでImageMagickとICUを使って画像に縦書きテキストを描画する / Draw vertical text in PHP using ImageMagick and ICU

PDFはこちら: https://drive.google.com/file/d/1frSl5X4296WzFm7V_thsYHcMCIWQt42J/view?usp=sharing

PHPerKaigi 2021 ( https://phperkaigi.jp/2021/ ) での発表「PHPでImageMagickとICUを使って画像に縦書きテキストを描画する」の資料です。

PHPで画像に縦書きテキストを描画する話です。
横書きテキストしか描画できないImageMagickと、ICU (Intl) を使って実現します。
発表では以下の内容を踏まえて縦書きテキストを画像に描画する方法とその要素技術についてお話しします。

- 縦書きテキストとは何か
- テキストを構成する文字について
- 文字を描画するためのフォントファイルについて
- フォントファイルを扱う時に便利なttxというツールについて
- PHPから使えるImageMagickについて
- PHPから使えるICUについて
- 文字列を折り返すということについて

2e72046813bf3dc01173fe54919a6d1d?s=128

cc4966

March 28, 2021
Tweet

Transcript

  1. SPLVSPLV!@ 1)1Ͱ*NBHF.BHJDLͱ*$6Λ࢖ͬ ͯը૾ʹॎॻ͖ςΩετΛඳը͢Δ 1)1FS,BJHJʙ5SBDL"ϨΪϡϥʔτʔΫ ʢ෼ʣ

  2. ࣗݾ঺հ SPLVSPLV w SPLVSPLV 5XJUUFS!@  w ීஈ΍ͬͯΔ͜ͱ w J04"OESPJEΞϓϦ։ൃ

    w "1*΍%#ઃܭɾ࣮૷ w Ϩίϝϯυ΍σʔλ෼ੳ΋গ͠
  3. ࣗݾ঺հ SPLVSPLV w SPLVSPLV 5XJUUFS!@  w ͳΜͰॎॻ͖ʁ w ձࣾͰॎॻ͖ؔ࿈ͷػೳΛ࡞ͬͨͷͰͦͷ͓࿩

    w झຯͰ΋5"5&EJUPSͱ͍͏ॎॻ͖ςΩετΤσΟλΛ࡞ͬͯΔ w J04 "OESPJE 8JOEPXT NBD04 6CVOUV աڈͷൃදΛͲ͏ͧ w J04%$ w %SPJE,BJHJ w J04%$
  4. খઆͷදࢴ࡞੒ػೳ w λΠτϧΛ౉͢ͱαʔόʔ͔Βจ ࣈೖΓͷදࢴը૾͕ฦΔ w ۃΊͯγϯϓϧͳػೳ w จࣈೖΕΛ1)1Ͱߦ͍ͬͯΔ ࠓճൃද͢Δ΋ͷ ΠϥετɺϚϯΨɺখઆ࡞඼ͷ౤ߘίϛϡχέʔγϣϯϓϥοτϑΥʔϜʮQJYJWʯ

  5. ൃදͷΞ΢τϥΠϯ 1)1Ͱ*NBHF.BHJDLͱ*$6Λ࢖ͬͯը૾ʹॎॻ͖ςΩετΛඳը͢Δ w ॎॻ͖ςΩετͱ͸Կ͔ w ςΩετΛ୺Ͱ͖Ε͍ʹંΓฦ͢ํ๏ʢ*$6ͷར༻ʣ w 1)1Ͱը૾ʹςΩετΛඳը͢Δํ๏ʢ*NBHF.BHJDLͷબ୒ཧ༝ʣ w ॎॻ͖͢ΔͨΊʹඞཁͳϑΥϯτ஌ࣝ

    w ·ͱΊ
  6. ൃදͷΞ΢τϥΠϯ 1)1Ͱ*NBHF.BHJDLͱ*$6Λ࢖ͬͯը૾ʹॎॻ͖ςΩετΛඳը͢Δ w ҎԼͷςΩετΛॎॻ͖Ͱදࣔ͢Δ͜ͱΛ໨ࢦͯ͠ίʔυΛަ͑ͳ͕Βൃද͠ ·͢ w ͋ͷΠʔϋτʔϰΥͷ͖͢ͱ͓ͬͨ෩ɺՆͰ΋ఈʹྫྷͨ͞Λ΋ͭ੨͍ͦ Βɺ͏͍ͭ͘͠৿Ͱ০ΒΕͨϞϦʔΦࢢɺ߫֎ͷ͗Β͗Βͻ͔Δ૲ͷ೾ɻ ͜Ε͸1)1FS,BJHJͷൃදɻ͋㿆 ͋ɻ㗫ᄖܹྭɻaOςετ

    w ࢖͏ϑΥϯτ͸/PUP4BOT4FSJG ೔ຊޠ 3FHVMBS Ͱ͢
  7. ॎॻ͖ςΩετͱ͸ w ςΩετΛॎॻ͖Ͱදࣔͨ͠΋ͷ w ςΩετΛදࣔ͢Δʹը૾Λ࡞Δ

  8. w ςΩετΛॎॻ͖Ͱදࣔͨ͠΋ͷ w ςΩετΛදࣔ͢Δʹը૾Λ࡞Δ ॎॻ͖ςΩετͱ͸ ͜ͷը૾Λ࡞Γ·͢

  9. ॎॻ͖ςΩετͱ͸ ςΩετΛදࣔ͢Δ w ॎॻ͖Ͱ΋ԣॻ͖Ͱ΋಺෦Ͱ΍͍ͬͯΔͷ͸جຊతʹಉ͡ w จࣈྻΛจࣈͣͭʹ෼ղ͢Δʢจࣈίʔυʣ w จࣈΛॱ൪ʹฒ΂͍ͯ͘ʢϑΥϯτʣ w ॎʹฒ΂Δͱॎॻ͖

    w ԣʹฒ΂Δͱԣॻ͖
  10. ॎॻ͖ςΩετͱ͸ จࣈΛදࣔ͢ΔͨΊͷཁૉٕज़ w จࣈίʔυ w ςΩετΛσʔλԽ͢Δϓϩτίϧ w 6OJDPEFͱ͔ w ϑΥϯτϑΝΠϧ

    w จࣈͷܗঢ়͕ऩೲ͞Ε͍ͯΔʢଟ͘ͷ৔߹Ξ΢τϥΠϯ৘ใʣ w ֤จࣈΛ഑ஔ͢Δํ๏͕هड़͞Εͨ΋ͷ
  11. ෳ਺ߦςΩετ w ࿦ཧతͳߦ͸վߦจࣈͰ؆୯ʹ۠ ੾Δ͜ͱ͕Ͱ͖Δ w ͋ͷΠʔϋτʔϰΥͷ͖͢ͱ ͓ͬͨ෩ɺՆͰ΋ఈʹྫྷͨ͞Λ ΋ͭ੨͍ͦΒɺ͏͍ͭ͘͠৿Ͱ ০ΒΕͨϞϦʔΦࢢɺ߫֎ͷ͗ Β͗Βͻ͔Δ૲ͷ೾ɻ͜Ε͸

    1)1FS,BJHJͷൃදɻ͋㿆 ͋ɻ㗫ᄖܹྭɻaOςετ จࣈྻΛંΓฦ͢ վߦaO͚ͩ൓ө͞Ε͍ͯΔ
  12. ෳ਺ߦςΩετ w ࿦ཧతͳߦ͸վߦจࣈͰ؆୯ʹ۠ ੾Δ͜ͱ͕Ͱ͖Δ w ͋ͷΠʔϋτʔϰΥͷ͖͢ͱ ͓ͬͨ෩ɺՆͰ΋ఈʹྫྷͨ͞Λ ΋ͭ੨͍ͦΒɺ͏͍ͭ͘͠৿Ͱ ০ΒΕͨϞϦʔΦࢢɺ߫֎ͷ͗ Β͗Βͻ͔Δ૲ͷ೾ɻ͜Ε͸

    1)1FS,BJHJͷൃදɻ͋㿆 ͋ɻ㗫ᄖܹྭɻaOςετ จࣈྻΛંΓฦ͢ ͸Έग़͍ͯ͠Δ
  13. ෳ਺ߦςΩετ w จࣈͣͭඳըͯ͠ંΓฦ͢ $dx = 0; $dy = 0; foreach(preg_split('//u',

    $text, -1, PREG_SPLIT_NO_EMPTY) as $part) { $metrics = $image->queryFontMetrics($draw, $part); $advance = $metrics['originX']; if ($dx + $advance > $width) { $dy += $line_height; $dx = 0; } $image->annotateImage($draw, $x + $dx, $y + $dy, 0, $part); $dx += $advance; if (preg_match("/\n/", $part)) { $dy += $line_height; $dx = 0; } } จࣈྻΛંΓฦ͢ ͜͜Ͱ͸ંΓฦ ͞Εͨ͘ͳ͍
  14. จࣈྻΛંΓฦ͢ 6OJDPEFͷંΓฦ͠ͷ࢓༷ w 6OJDPEF4UBOEBSE"OOFY 6"9  w 6OJDPEF-JOF#SFBL"MHPSJUIN w ςΩετͷͲ͜ͳΒվߦͯ͠΋͍͍ͷ͔ɺ͍ͯ͸͍͚ͳ͍ͷ͔

    w ςΩετͷͲ͜Ͱվߦ͠ͳ͚Ε͹͍͚ͳ͍ͷ͔ʢaOͱ͔ͷվߦه߸ʣ
  15. 1)1͔Β࢖͑Δ*$6ʹ͍ͭͯ *OUFSOBUJPOBM$PNQPOFOUGPS6OJDPEF w *$6͸6OJDPEFͷ৘ใ΍ΞϧΰϦζϜΛར༻Ͱ͖ΔϥΠϒϥϦ w 1)1Ҏ߱ಉࠝ͞ΕΔࠃࡍԽ༻֦ுϞδϡʔϧΛ௨ͯ͠ར༻Մೳ w ͜ΕΛ࢖ͬͯվߦՄೳͳҐஔΛऔಘ͢Δ͜ͱ͕Ͱ͖·͢ // Before

    foreach(preg_split('//u', $text, -1, PREG_SPLIT_NO_EMPTY) as $part) {...} // After $it = IntlBreakIterator::createLineInstance('jp'); $it->setText($text); foreach ($it->getPartsIterator() as $part) {...}
  16. ෳ਺ߦςΩετ w վߦՄೳͳҐஔͰંΓฦ͢ จࣈྻΛંΓฦ͢ ୯ޠ͕෼அ͞Ε ͳ͘ͳͬͨ ʮͬʯ͕ߦ಄Ͱ͸ ͳ͘ͳͬͨ ʮɻʯ͕ߦ಄ʹͳΔ ͷ͕ճආ͞Εͨ

  17. 1)1ͰςΩετΛඳը͢Δ ࢖͑Δ΋ͷ w ը૾ʹςΩετΛඳը͢Δखஈ w (NBHJDL w *NBHF.BHJDL w (%

    w ͦͷଞʢࣗલͰ࣮૷ͳͲʣ
  18. 1)1ͰςΩετΛඳը͢Δ (NBHJDLͷςΩετඳը w (NBHJDL%SBXBOOPUBUF w ςΩετΛඳը͢Δؔ਺ w ໰୊఺ w ඳըςΩετͷαΠζ͕Θ͔Βͳ͍

    w ͭ·ΓંΓฦ͠ͷॲཧ͸Ͱ͖ͳ͍
  19. 1)1ͰςΩετΛඳը͢Δ *NBHF.BHJDLͷςΩετඳը w *NBHJDLBOOPUBUF*NBHF w ςΩετΛඳը͢Δؔ਺ w *NBHJDLRVFSZ'POU.FUSJDT w ඳըςΩετͷϝτϦΫε৘ใΛܭࢉ͢Δؔ਺

    w ςΩετͷ෯΋औಘͰ͖Δ
  20. 1)1ͰςΩετΛඳը͢Δ (%ͷςΩετඳը w JNBHFGUUFYU w ςΩετΛඳը͢Δؔ਺ w JNBHFGUCCPY w ςΩετΛғ͏௕ํܗͷ࢛۱ͷ࠲ඪΛऔಘ͢Δؔ਺

    w ##PY CPVOEJOHCPY ͱ͋Δ͕ਫฏํ޲͸ςΩετͷ෯ʢ༨നΛؚΉʣ
  21. 1)1ͰςΩετΛඳը͢Δ (%ͷςΩετඳըͷ஫ҙࣄ߲ w σϑΥϧτ%1*ͳͷͰϑΥϯταΠζʢ୯Ґ͕QUʣ͸QJYFMΛഒ͢Δ w EQJQJYFMJODIQJYFMQU JODIQU  w QJYFMQUºQUQJYFM

    w JNBHFGUCCPY͸##PYΛฦ͞ͳ͍ w Zํ޲͸CPVOEJOHCPYʢ༨നΛؚ·ͳ͍ʣ w Yํ޲͸ςΩετͷ෯ʢ༨നΛؚΉʣ
  22. *NBHF.BHJDLͷબ୒ཧ༝ w (%͕#.1ʹ͔͠ରԠ͍ͯ͠ͳ͍ͨΊ*NBHF.BHJDLΛબ୒ʢݱࡏʣ w #.1 #BTJD.VMUJMJOHVBM1MBOF جຊଟݴޠ໘ w ίʔυϙΠϯτ͕6 d6

    ''''ͷCJUͰදݱͰ͖Δൣғ (%Ͱ͸ৗ༻׽ࣈͷ㗫 6 #' ͕จࣈԽ͚ *NBHF.BHJDL
  23. ॎॻ͖ͱԣॻ͖ w ॎॻ͖Ͱ͸จࣈΛॎʹฒ΂Δ foreach(preg_split('//u', $part, -1, PREG_SPLIT_NO_EMPTY) as $c) {

    // advance͸ϑΥϯτϑΝΠϧͷvmtx ςʔϒϧ͔Β $advance = $font_size; $image->annotateImage($draw, $x + $dx, $y + $dy, 0, $c); $dy += $advance; } ॎॻ͖ͱจࣈͷ޲͖
  24. ॎॻ͖ͱԣॻ͖ w ॎॻ͖Ͱ͸จࣈΛॎʹฒ΂Δ ॎॻ͖ͱจࣈͷ޲͖ ԣ౗͠ʹͳͬͯ΄͍͠

  25. ॎॻ͖ͱจࣈͷ޲͖ 6OJDPEFͷॎॻ͖ͷ࢓༷ w 6OJDPEF4UBOEBSE"OOFY 6"9  w 6OJDPEF7FSUJDBM5FYU-BZPVU w 6ͦͷ··දࣔ͢Δ΋ͷʢ೔ຊޠͷจࣈͱ͔ʣ

    w 3ӈ౗͠Ͱදࣔ͢Δ΋ͷʢΞϧϑΝϕοτͱ͔ʣ w 5Vͦͷ··දࣔ͢Δ͕ผͷάϦϑ͕ඞཁʢখจࣈͷฏԾ໊ΧλΧφʣ w 5Sӈ౗͠Ͱදࣔ͢Δ͕ผͷάϦϑ͕ඞཁʢ௕Իه߸ʣ શͯͷจࣈΛ͜ͷͭ Ͱ෼ྨ͍ͯ͠Δ
  26. ॎॻ͖ͱԣॻ͖ w 3ͳΒ౓ճస͢Δ if (!isR($c)) { $image->annotateImage($draw, $x + $dx,

    $y + $dy, 0, $c); } else { $image->annotateImage($draw, $x + $dx, $y + $dy, 90, $c); } w 5S͸ϑΥϯτͷ࢓ࣄͳͷͰ์ஔ ॎॻ͖ͱจࣈͷ޲͖ ͳΜ͔ͣΕͯΔ
  27. ॎॻ͖ͱϕʔεϥΠϯ 6OJDPEFͷॎॻ͖ͷ࢓༷ w ςΩετ͸ϕʔεϥΠϯ CBTFMJOF Λج४ʹඳը͞ΕΔ w ϕʔεϥΠϯ͸ඳըྖҬͷ୺Ͱ͸ͳ͍ IUUQTXXXGSFFUZQFPSHGSFFUZQFEPDTUVUPSJBMTUFQIUNMTFDUJPOΑΓ ج४఺

  28. ॎॻ͖ͱϕʔεϥΠϯ 6OJDPEFͷॎॻ͖ͷ࢓༷ w ςΩετ͸ϕʔεϥΠϯ CBTFMJOF Λج४ʹඳը͞ΕΔ w ϕʔεϥΠϯ͸ඳըྖҬͷ୺Ͱ͸ͳ͍ ج४఺Λἧ͑ΔͱͣΕΔ

  29. ॎॻ͖ͱϕʔεϥΠϯ 6OJDPEFͷॎॻ͖ͷ࢓༷ w ςΩετ͸ϕʔεϥΠϯ CBTFMJOF Λج४ʹඳը͞ΕΔ w ϕʔεϥΠϯ͸ඳըྖҬͷ୺Ͱ͸ͳ͍ ճసͳ͠ͷج४఺ ճస͋Γͷج४఺

  30. ॎॻ͖ͱԣॻ͖ // ӈ্࠲ඪΛࢦఆ͢Δ͜ͱʹ͢Δ if (!isR($c)) { $image->annotateImage($draw, $x - $font_size,

    $y + $font_size * 880 / 1000, 0, $c); } else { $image->annotateImage($draw, $x - $font_size * 880 / 1000, $y, 90, $c); } ॎॻ͖ͱจࣈͷ޲͖ BTDFOEFS VOJUTQFSFN ϑΥϯτ͝ͱͷ஋
  31. จࣈͱ͸ w ॎॻ͖Ͱ͸จࣈͣͭඳը w ʮ͋㿆 ʯ͸ίʔυϙΠϯτͩ ͚Ͳจࣈѻ͍͍ͨ͠ ॻهૉΫϥελ ͜Ε͸͙͢௚Δ

  32. ॻهૉΫϥελ 6OJDPEFͷจࣈͷ࢓༷ w 6OJDPEF4UBOEBSE"OOFY 6"9  w 6OJDPEF5FYU4FHNFOUBUJPO w ͜Ε΋*$6Λར༻͢Ε͹ॻهૉΫϥελ΋ղܾ

    // Before foreach(preg_split('//u', $part, -1, PREG_SPLIT_NO_EMPTY) as $c) {...} // After $it = IntlBreakIterator::createCharacterInstance('jp'); $it->setText($part); foreach($it->getPartsIterator() as $c) {...}
  33. ॎॻ͖༻άϦϑ w ॻهૉΫϥελ͸όϥόϥʹͤ ͣճͰඳը͢Δ ॎॻ͖ͱϑΥϯτ ௚ͬͨ

  34. ॎॻ͖༻άϦϑ w ϑΥϯτͷதͷจࣈʢཁૉɺ ୯ҐʣΛάϦϑͱ͍͏ w ϑΥϯτ͸ԣॻ͖༻ͷάϦϑͱ ॎॻ͖༻ͷάϦϑͷ྆ํΛ࣋ͭ ॎॻ͖ͱϑΥϯτ খจࣈ ௕Իه߸

    ۟ಡ఺
  35. ॎॻ͖ͱϑΥϯτ DNBQςʔϒϧ w ϑΥϯτ͸จࣈίʔυͷίʔυϙΠϯτ͔ΒάϦϑͷ*%΁ͷϚοϓΛ࣋ͭ w DNBQςʔϒϧ ͋ 6  ɻ

    6  ͋ (*% ɻ (*% Ố (*% ॎॻ͖༻ͷάϦϑ
  36. ॎॻ͖ͱϑΥϯτ (46# WFSUWSUS w (46#ςʔϒϧ w ϦΨνϟͳͲಛఆͷάϦϑྻΛผͷάϦϑྻʹม׵͢ΔػೳΛఏڙ͢Δ w ԣॻ͖༻ͷάϦϑ*%Λॎॻ͖༻ͷάϦϑ*%ʹม׵͢Δม׵ςʔϒϧ΋͋Δ w

    WFSUWSUSʢWSU΋͋Δ͕ࠓճ͸࢖Θͳ͍ʣ
  37. ॎॻ͖ͱϑΥϯτ 1)1ͱॎॻ͖ w (%΋*NBHF.BHJDL΋ಛఆͷϑΥϯτͷGFBUVSFΛ༗ޮʹ͢Δ͜ͱ͸Ͱ͖ͳ͍ w ͭ·Γॎॻ͖༻ͷάϦϑ͕ඳը͞ΕΔ͜ͱ͸ͳ͍

  38. ϑΥϯτΛվ଄͠·͢

  39. ॎॻ͖ͱϑΥϯτ UUYͱ͍͏πʔϧ w UUY w ϑΥϯτϑΝΠϧͱYNMͷ૬ޓม׵πʔϧ w DNBQ΍(104ͷத਎ΛYNMʹͯ͠ಡΊΔ w DNBQͷม׵ઌͷάϦϑ*%ʹWFSUWSUSͷม׵ςʔϒϧΛద༻͢Δ

    w ԣॻ͖ͷάϦϑͷ୅ΘΓʹॎॻ͖ͷάϦϑ͕ඳը͞ΕΔ
  40. ॎॻ͖ͱϑΥϯτ DNBQςʔϒϧ w ϑΥϯτ͸จࣈίʔυͷίʔυϙΠϯτ͔ΒάϦϑͷ*%΁ͷϚοϓΛ࣋ͭ w DNBQςʔϒϧ ͋ 6  ɻ

    6  ͋ (*% ɻ (*% Ố (*% ॎॻ͖༻ͷάϦϑ
  41. 1)1Ͱ *NBHF.BHJDLͱ*$6 Λ࢖ͬͯը૾ʹॎॻ͖ ςΩετΛඳը͢Δ

  42. ·ͱΊ 1)1Ͱ*NBHF.BHJDLͱ*$6Λ࢖ͬͯը૾ʹॎॻ͖ςΩετΛඳը͢Δ w 1)1Ͱૉ௚ʹॎॻ͖ςΩετΛඳը͢Δํ๏͸ͳ͍ w վߦҐஔ΍ॻهૉΫϥελͷ൑ఆ͸*$6͕࢖͑ΔʢࠃࡍԽ༻֦ுϞδϡʔϧʣ w 1)1ͰςΩετඳը͢ΔͳΒ*NBHJDL͕Φεεϝʢ#.1֎ͷจࣈ΋࢖͑Δʣ w ॎॻ͖͢ΔͳΒվ଄ϑΥϯτΛ༻ҙ͠Α͏ʢDNBQΛॻ͖׵͑Δʣ

    w ϑΥϯτͷඳը͚ͩ΍ͬͯ΋Βͬͯɺจࣈͷճసɾ഑ஔ͸ࣗલͰ΍Γ·͠ΐ͏
  43. ࠓճ࿩͞ͳ͔ͬͨ͜ͱ ࡉ͔͍͜ͱ w Χϥʔֆจࣈʹ͸ରԠ͢ΔͳΒผ్࣮૷͢Δඞཁ͕͋Δ w ҟମࣈηϨΫλʹରԠ͢ΔͳΒDNBQΛ͞Βʹվ଄͢Δඞཁ͕͋Δ w ॎॻ͖ͷࡍͷจࣈͷ෯͸WNUYςʔϒϧ͔Βநग़͓ͯ͘͠ඞཁ͕͋Δ w ॎॻ͖ͷࡍͷจࣈͷඳըҐஔ͸WNUYςʔϒϧ͔Βநग़͓ͯ͘͠ඞཁ͕͋Δ

    w 5SVF5ZQFͰͳ͚Ε͹703(ςʔϒϧͰ΋ྑ͍ w ॎॻ͖ͷΧʔχϯά৘ใ͸WLSOςʔϒϧ͔Βநग़͓ͯ͘͠ඞཁ͕͋Δ