xlogI125’s blog

パソコン作業を効率化したい

Acrobat Reader: 長方形の対角線位置に直線を追加

バツ印に見える注釈を作成したいので、長方形の対角線位置に直線を追加する方法を考える。

// テスト環境: Acrobat Reader (2024年3月頃), Windows 11
// ファイル名: %APPDATA%\Adobe\Acrobat\Privileged\DC\JavaScripts\test.js
// エンコード: ANSI

// 起動時に毎回JavaScriptデバッガーを表示
console.show();
console.println("// コンソールでの実行はテキストを範囲選択して Ctrl + Enter");
console.println("// app.getPath('user', 'javascript');");
// #=> /C/Users/xxx/AppData/Roaming/Adobe/Acrobat/Privileged/DC/JavaScripts

// メニューに登録
app.addSubMenu({ cParent: "Help", nPos: 0, cName: "MyTest", cUser: "テスト" });
app.addSubMenu({ cParent: "Help", nPos: 1, cName: "-",      cUser: ""       });

app.addMenuItem({
  cParent: "MyTest", 
  cName  : "MyTestShowConsole", 
  cUser  : "コンソールを表示", 
  cEnable: "event.rc = true;", 
  cExec  : "(() => { console.show(); })();"
});

app.addMenuItem({
  cParent: "MyTest", 
  cName  : "-", 
  cExec  : ""
});

app.addMenuItem({
  cParent: "MyTest", 
  cName  : "MyTestShowAnnotProps", 
  cUser  : "注釈のプロパティを表示", 
  cEnable: "event.rc = (event.target != null);", 
  cExec  : util.printf("(%s)();", (() => {
    console.clear();
    const annots = this.selectedAnnots;
    if (annots === undefined) return;
    var str = "";
    annots.forEach((annot, i) => {
      const prop = annot.getProps();
      for (name in prop) {
        switch (name) {
          case "points":
            var x1mm = prop[name][0][0] * 25.4 / 72;
            var y1mm = prop[name][0][1] * 25.4 / 72;
            var x2mm = prop[name][1][0] * 25.4 / 72;
            var y2mm = prop[name][1][1] * 25.4 / 72;
            str += util.printf("%02d %s : [[%.1f, %.1f], [%.1f, %.1f]] // mm\r", i, name, x1mm, y1mm, x2mm, y2mm);
            break;
          case "popupRect":
          case "rect":
            if (prop[name] !== undefined) {
              var x1mm = prop[name][0] * 25.4 / 72;
              var y1mm = prop[name][1] * 25.4 / 72;
              var x2mm = prop[name][2] * 25.4 / 72;
              var y2mm = prop[name][3] * 25.4 / 72;
              str += util.printf("%02d %s : [%.1f, %.1f, %.1f, %.1f] // mm\r", i, name, x1mm, y1mm, x2mm, y2mm);
              break;
            }
          default:
            str += util.printf("%02d %s : %s\r", i, name, prop[name]);
        }
      }
    });
    console.println(str);
    console.show();
  }).toString())
});

app.addMenuItem({
  cParent: "MyTest", 
  cName  : "-", 
  cExec  : ""
});

app.addMenuItem({
  cParent: "MyTest", 
  cName  : "MyTestAddLine", 
  cUser  : "長方形の対角線位置に直線を追加", 
  cEnable: "event.rc = (event.target != null);", 
  cExec  : util.printf("(%s)();", (() => {
    // 選択された注釈を取得
    const annots = this.selectedAnnots;
    if (annots === undefined) return;
    annots.forEach(annot => {
      switch (annot.type) {
        case "Square"  : break; // 長方形
        case "FreeText": break; // テキストボックス
        default        : return;
      }
      // 形状が長方形である注釈のプロパティを取得
      const propRect = annot.getProps();
      // 線のプロパティを設定
      var propLine = {
        type        : "Line", 
        page        : propRect.page, 
        points      : [[0, 0], [1, 1]], 
        doCaption   : false, 
        leaderExtend: 0, 
        leaderLength: 0, 
        // 表示方法
        arrowBegin : "None", 
        arrowEnd   : "None", 
        style      : "S", 
        width      : propRect.width, 
        strokeColor: propRect.strokeColor, 
        fillColor  : propRect.fillColor, 
        opacity    : propRect.opacity, 
        // 一般
        author : propRect.author, 
        subject: propRect.subject, 
        modDate: propRect.modDate, 
        // ロックなど
        lock: false, readOnly: false, hidden: false, noView: false, toggleNoView: false, print: true, delay: false
      };
      // 線を追加
      const annotLine1 = this.addAnnot(propLine);
      const annotLine2 = this.addAnnot(propLine);
      // 線の座標を変更
      const [x1, y1, x2, y2] = propRect.rect;
      annotLine1.points = [[x1, y1], [x2, y2]];
      annotLine2.points = [[x1, y2], [x2, y1]];
    });
  }).toString())
});

PowerShell練習 Select-ObjectのPropertyパラメーター 2

Select-Objectでプロパティを指定してから表示

# PowerShell 5.1, Windows 11 (2024年2月頃)
Set-StrictMode -Version Latest

Add-Type -Language CSharp -Namespace Win32API -Name Shlwapi -MemberDefinition @'
  [DllImport("Shlwapi.dll", EntryPoint = "StrCmpLogicalW", CharSet = CharSet.Unicode)]
  public static extern int StrCmpLogicalW([MarshalAs(UnmanagedType.LPWStr)] string psz1, [MarshalAs(UnmanagedType.LPWStr)] string psz2);
'@

# 配列
$psObjs = [PSObject[]]@(
  [PSCustomObject]@{ Name="05 file.txt" ; A="ValA" }
  [PSCustomObject]@{ Name="050 file.txt"; B="ValB" }
  [PSCustomObject]@{ Name="1 file.txt"  ; C="ValC" }
  [PSCustomObject]@{ Name="10 file.txt" ; D="ValD" }
  [PSCustomObject]@{ Name="100 file.txt" }
)

# 配列をソート
[System.Array]::Sort($psObjs, { [Win32API.Shlwapi]::StrCmpLogicalW($args[0].Name, $args[1].Name) } -as [System.Comparison[PSObject]] )

# プロパティ名
$propNames = $psObjs.ForEach({ $_.PSObject.Properties.ForEach({ $_.Name }) }) | Sort-Object | Get-Unique

$propNames = @("Name") + @($propNames -ne "Name")

# 表示
$psObjs | ConvertTo-Csv -NoTypeInformation | ConvertFrom-Csv | Format-Table -AutoSize | Out-String | Write-Host

# Name         C
# ----         -
# 1 file.txt   ValC
# 05 file.txt
# 10 file.txt
# 050 file.txt
# 100 file.txt

# Select-Objectでプロパティを指定してから表示
$psObjs | Select-Object -Property $propNames | ConvertTo-Csv -NoTypeInformation | ConvertFrom-Csv | Format-Table -AutoSize | Out-String | Write-Host

# Name         A    B    C    D
# ----         -    -    -    -
# 1 file.txt             ValC
# 05 file.txt  ValA
# 10 file.txt                 ValD
# 050 file.txt      ValB
# 100 file.txt
# PowerShell 5.1, Windows 11 (2024年2月頃)
Set-StrictMode -Version Latest

Add-Type -Language CSharp -Namespace Win32Functions -Name Shlwapi -MemberDefinition @'
  [DllImport("Shlwapi.dll", EntryPoint = "StrCmpLogicalW", CharSet = CharSet.Unicode)]
  public static extern int StrCmpLogicalW([MarshalAs(UnmanagedType.LPWStr)] string psz1, [MarshalAs(UnmanagedType.LPWStr)] string psz2);
'@

# 並べ替え後のFileSystemInfoの配列を取得
$fsis = & {
  param ([HashTable]$Splatting)

  # Get-ChildItemにてFileInfoとDirectoryInfoを取得
  $fsis = Get-ChildItem @Splatting

  # AddRangeを使うため
  $fsiList = [System.Collections.Generic.List[System.IO.FileSystemInfo]]::new()

  # Comparison
  $cmpName = { param ($x, $y); return [Win32Functions.Shlwapi]::StrCmpLogicalW($x.Name, $y.Name) }
  $comparisonGroupInfo     = [System.Comparison[Microsoft.PowerShell.Commands.GroupInfo]]$cmpName
  $comparisonDirectoryInfo = [System.Comparison[System.IO.DirectoryInfo]]$cmpName
  $comparisonFileInfo      = [System.Comparison[System.IO.FileInfo]]$cmpName

  # Group-ObjectにてPSParentPathでグループ化
  $grs = [Microsoft.PowerShell.Commands.GroupInfo[]]@($fsis | Group-Object -Property PSParentPath)

  # GroupInfoの配列をArray.Sortで並べ替え
  [System.Array]::Sort($grs, $comparisonGroupInfo)

  for ($i = 0; $i -lt $grs.Length; $i++) {
    $fsisGr = [System.IO.FileSystemInfo[]]@($grs[$i].Group)

    # フォルダ
    $disGr = [System.IO.DirectoryInfo[]]@($fsisGr.Where({ [System.IO.Directory]::Exists($_.FullName) }))
    [System.Array]::Sort($disGr, $comparisonDirectoryInfo)
    $fsiList.AddRange($disGr)

    # ファイル
    $fisGr = [System.IO.FileInfo[]]@($fsisGr.Where({ [System.IO.File]::Exists($_.FullName) }))
    [System.Array]::Sort($fisGr, $comparisonFileInfo)
    $fsiList.AddRange($fisGr)
  }

  if ($fsiList.Count -ne $fsis.Length) {
    throw
  }

  return (, [System.IO.FileSystemInfo[]]$fsiList)
} -Splatting @{ LiteralPath="$env:USERPROFILE"; Force=$true; Recurse=$true; Depth=3 }

# 並べ替え後のプロパティ名を取得
$propertyNames = & {
  param ([PSObject[]]$PSObjects)

  # UnionWithを使うため
  $propNameSet = [System.Collections.Generic.HashSet[string]]::new()

  # 各要素のプロパティ名をHashSetに追加
  ($PSObjects -ne $null).ForEach({ $propNameSet.UnionWith([string[]]$_.PSObject.Properties.ForEach({ $_.Name })) })

  # 配列に変換
  $propNames = [string[]]$propNameSet

  # 配列の並べ替え
  $cmp = [System.Comparison[string]]{ param ($x, $y); return [Win32Functions.Shlwapi]::StrCmpLogicalW($x, $y) }
  [System.Array]::Sort($propNames, $cmp)

  return (, $propNames)
} -PSObjects $fsis

# スプラッティング
$splatting = Invoke-Command -NoNewScope -ScriptBlock $([ScriptBlock]::Create(@'
@{
  Property = @(
    @{ Name="_ParentPath"; Expr={ Convert-Path -LiteralPath $_.PSParentPath } }, 
    @{ Name="_Name"      ; Expr={ $_.Name } }
  ) + $propertyNames
  ExcludeProperty = @("Target", "VersionInfo")
}
'@))

$fsis | Select-Object @splatting | ConvertTo-Csv -NoTypeInformation | Set-Clipboard


dy100ms.hatenadiary.jp

PowerShell: フォームにドラッグされたファイルのパスを取得

フォームでのドロップ操作を省略し、ドラッグ操作とクリック操作を考える。
普通にショートカット(*.lnk)アイコンへのドラッグ&ドロップのほうが便利な気がする。

# [テスト環境]
#  PowerShell 5.1, Windows 11 (2024年2月頃)
# [スクリプトファイル(.ps1)]
#  <ファイル名>
#   "%USERPROFILE%\Desktop\test.ps1"
#  <エンコード>
#   UTF-8 (BOM付き)
# [ショートカット(.lnk)]
#  <リンク先(T)>
#   PowerShell.exe -NoLogo -NoExit -NoProfile -ExecutionPolicy RemoteSigned -File "%USERPROFILE%\Desktop\test.ps1"
#  <作業フォルダー(S)>
#   ""

$ErrorActionPreference = "Stop"
$VerbosePreference = "Continue"

Set-StrictMode -Version Latest

Add-Type -AssemblyName System.Windows.Forms

Add-Type -Language CSharp -TypeDefinition @'
using System.Runtime.InteropServices;
namespace Win32API {
  public class Shlwapi {
    [DllImport("Shlwapi.dll", EntryPoint = "StrCmpLogicalW", CharSet = CharSet.Unicode)]
    public static extern int StrCmpLogicalW([MarshalAs(UnmanagedType.LPWStr)] string psz1, [MarshalAs(UnmanagedType.LPWStr)] string psz2);
  }
}
'@

# Comparison
$cmp = [System.Comparison[string]]{
  param ([string]$x, [string]$y)
  return [Win32API.Shlwapi]::StrCmpLogicalW($x, $y)
}

$paths = [string[]]$args
[System.Array]::Sort($paths, $cmp)
$paths.ForEach({ [PSCustomObject]@{Type="lnk";Value=$_} }) | Out-String | Write-Verbose

$form = [System.Windows.Forms.Form]::new()
$form.AllowDrop = $true

$form.add_DragEnter({
  try {
    # フォルダやテキストなどのドラッグ操作も受け付けるけど気にしない
    $_.Effect = [System.Windows.Forms.DragDropEffects]::Copy
    $list = [System.Collections.Generic.List[string]][string[]]$_.Data.GetFileDropList()
    $list.Sort($cmp)
    $list | Select-Object -Property @{Name="Type";Expr={"drag"}}, @{Name="Value";Expr={$_}} | Out-String | Write-Verbose
    $script:paths = [string[]]$list
  }
  catch {
    Write-Error -ErrorRecord $_ -ErrorAction Continue
  }
})

$form.add_Click({
  $this.Close()
})

[void]$form.ShowDialog()
$form.Dispose()

$paths

dy100ms.hatenadiary.jp