LaTeX 没有像 Word 那样自带中文字数统计功能,加上 LaTeX 源文件中有许多控制字符,不能通过文件大小获知其中有多少汉字。为此我用C写了一个统计中文字数的小工具,名为 cwc ,即 chinese word counter。这个程序只有 count_files() 函数使用了 Windows API,稍作修改就能移植到 Linux/Unix 下。



#include <stdio.h>

#include <wchar.h>

#include <windows.h>

int total = 0; // total chinese characters


// UNICODE version word counter

void word_count_u(FILE* pf)

{

    int w = 0, b = 2;

    wint_t c;

    while((c = getwc(pf)) != WEOF) {

        b += 2;         // byte count

        if (c > 127) {  // 中文字符

            w++;        // char count

        }

    }

   

    printf("%10d \t %10d\n", w, b);

    total += w;

}


// word counter

void word_count(const char* file)

{

    int w = 0, b = 0;

    int c;

    int unicode = 0;

   

    FILE *pf = fopen(file, "rb");

    if (NULL == pf) {

        return;

    }

    printf ("%20s : ", file);

    

    // 判断是否为 UNICODE 文件

    if ((c = getc(pf)) == 0xff) {

        int cc;

        if ((cc = getc(pf)) == 0xfe) {

            unicode = 1;

            printf("UNICODE");

            word_count_u(pf);

        }

        else {

            fseek(pf, 0, SEEK_SET);

        }

    }

    else {

        ungetc(c, pf);

    }

   

    if (!unicode) {

        printf("       ");

        while((c = getc(pf)) != EOF) {

            b++; // byte count

            if (c > 127) { // 中文字符

                w++; // char count

                b++; // 每个中文字符占两字节

                if ((c = getc(pf)) == EOF)

                    break;

            }

        }

   

        printf("%10d \t %10d\n", w, b);

        total += w;

    }

    fclose(pf);

}

void count_files(const char* file)

{

    WIN32_FIND_DATA FindFileData;

    HANDLE hFind = FindFirstFile(file, &FindFileData);

    if (INVALID_HANDLE_VALUE == hFind) {

        fprintf(stderr, "Can't find %s\n", file);

        return ;

    }

    do {

        if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) {

            continue;

        }

        if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {

            // do nothing

        }

        else {

            word_count(FindFileData.cFileName);

        }

    } while (FindNextFile(hFind, &FindFileData));

    FindClose(hFind);

}

int main(int argc, char* argv[])

{

    if (1 == argc) {

        // default: count all .tex and .txt files in current dir

        count_files("*.tex");

        count_files("*.txt");

    }

    else {

        // get filenames from command line arguments

        while (--argc > 0) {

            count_files(argv[argc]);

        }

    }

    printf("  Total : %d", total);

    return 0;

}


另外一个常见的问题是,用 dvipdfm 生成 PDF 文件时,经常忘了在 Acrobat 中关闭这个文件,而导致出现以下错误提示:


E:\Projects\SlideWindowReport>dvipdfm SlideWindow

SlideWindow.dvi -> SlideWindow.pdf

Unable to open SlideWindow.pdf

Output file removed.


为此我写了一个专门用来关闭 Acrobat 中的 PDF 文件的小工具,名为 closepdf,原理是通过 DDE 告诉 Acrobat 关闭某个 PDF 文件。这个小工具的灵感来自 WinEdt 的自动关闭功能(我跟踪它的宏代码得知如何用 DDE 控制 Acrobat),代码改自 MSDN 的一篇文章。我完全不懂 DDE,不过这不妨碍程序的正常工作:)


有了这个小工具,我就可以写一个 batch file 来编译生成并阅读 PDF 文件:


rem this is m.bat

latex %1

latex %1

closepdf %1.pdf

dvipdfm %1 %2 %3 %4 %5

start %1.pdf


代码如下:

// close the user specified pdf file in Acrobat Reader


// ref. Microsoft Knowledge Base Article - 279721

// HOWTO: Use Dynamic Data Exchange (DDE) with Word and Excel from Visual C++


// modified by Solstice 2004/01/2


#define WIN32_LEAN_AND_MEAN  // Exclude rarely-used stuff from Windows headers

#define STRICT

#include <windows.h>

#include <ddeml.h>

#include <stdio.h>

#include <assert.h>

#include <stdlib.h>

HDDEDATA CALLBACK DdeCallback(

    UINT uType,     // Transaction type.

    UINT uFmt,      // Clipboard data format.

    HCONV hconv,    // Handle to the conversation.

    HSZ hsz1,       // Handle to a string.

    HSZ hsz2,       // Handle to a string.

    HDDEDATA hdata, // Handle to a global memory object.

    DWORD dwData1,  // Transaction-specific data.

    DWORD dwData2)  // Transaction-specific data.

{

    return 0;

}

void DDEExecute(DWORD idInst, HCONV hConv, char* szCommand)

{

    HDDEDATA hData = DdeCreateDataHandle(idInst, (LPBYTE)szCommand,

                               lstrlen(szCommand)+1, 0, NULL, CF_TEXT, 0);

    if (hData==NULL)   {

        printf("Command failed: %s\n", szCommand);

    }

    else    {

        DdeClientTransaction((LPBYTE)hData, 0xFFFFFFFF, hConv, 0L, 0,

                             XTYP_EXECUTE, TIMEOUT_ASYNC, NULL);

    }

}

void DDERequest(DWORD idInst, HCONV hConv, char* szItem, char* sDesc)

{

    HSZ hszItem = DdeCreateStringHandle(idInst, szItem, 0);

    HDDEDATA hData = DdeClientTransaction(NULL,0,hConv,hszItem,CF_TEXT,

                                 XTYP_REQUEST,5000 , NULL);

    if (hData == NULL)

    {

        printf("Request failed: %s\n", szItem);

    }

    else

    {

        char szResult[255];

        DdeGetData(hData, (unsigned char *)szResult, 255, 0);

        printf("%s%s\n", sDesc, szResult);

    }

}

void DDEPoke(DWORD idInst, HCONV hConv, char* szItem, char* szData)

{

    HSZ hszItem = DdeCreateStringHandle(idInst, szItem, 0);

 DdeClientTransaction((LPBYTE)szData, (DWORD)(lstrlen(szData)+1),

                          hConv, hszItem, CF_TEXT,

                          XTYP_POKE, 3000, NULL);

    DdeFreeStringHandle(idInst, hszItem);

}

int get_error(const char* filename)

{

 HANDLE hFile = CreateFile(filename,

     GENERIC_WRITE,

     FILE_SHARE_READ,

     NULL,

     OPEN_EXISTING,

     FILE_ATTRIBUTE_NORMAL,

     NULL);

   

    if (INVALID_HANDLE_VALUE == hFile) {

         return GetLastError();

    } else {

     CloseHandle(hFile);

     return 0;

    }

}

bool is_file_locked(const char* filename)

{   

 return (get_error(filename) == 32);

}

bool is_file_exists(const char* filename)

{

 return (get_error(filename) != 2);

}

int main(int argc, char* argv[])

{

    char szApp[] = "acroview";

    char szTopic[] = "control";

    char szCloseAll[] = "[CloseAllDocs()]";

    char full_file_name[MAX_PATH];

 const char* file_to_close = argv[1];

   

    if (argc == 1) {

     // close all pdf files

 } else { // close the specified file

  if (!is_file_exists(file_to_close)) {

   printf("%s doesn't exists.\n", file_to_close);

   return 1;

  }

  if (is_file_locked(file_to_close)) {

   printf("%s is open, trying to close it...\n", file_to_close);

  } else {

   printf("%s is closed.\n", file_to_close);

   return 0;

  }

  assert (GetFullPathName(

      file_to_close,

      sizeof(full_file_name),

      full_file_name,

      NULL

  ) > 0);

  //printf("Full name : \"%s\"\n", full_file_name);

 }

   

    //[DocOpen(""%P\%N.pdf"")]

    //[DocClose(""%P\%N.pdf"")]

    //DDE Initialization

    DWORD idInst=0;

    UINT iReturn;

    iReturn = DdeInitialize(&idInst, (PFNCALLBACK)DdeCallback,

                            APPCLASS_STANDARD | APPCMD_CLIENTONLY, 0 );

    if (iReturn!=DMLERR_NO_ERROR)

    {

        printf("DDE Initialization Failed: 0x%04x\n", iReturn);

        Sleep(1500);

        return 0;

    }

    //DDE Connect to Server using given AppName and topic.

    HSZ hszApp, hszTopic;

    HCONV hConv;

    hszApp = DdeCreateStringHandle(idInst, szApp, 0);

    hszTopic = DdeCreateStringHandle(idInst, szTopic, 0);

    hConv = DdeConnect(idInst, hszApp, hszTopic, NULL);

    DdeFreeStringHandle(idInst, hszApp);

    DdeFreeStringHandle(idInst, hszTopic);

    if (hConv == NULL)

    {

        printf("DDE Connection Failed.\n");

        Sleep(500); DdeUninitialize(idInst);

        return 0;

    }

    else

    {

     printf("DDE Connection to Acrobat Reader succeed.\n");

     Sleep(50);

    }

   

    if (argc == 1) {

     // close all pdf files

     //Execute commands/requests specific to the DDE Server.

     DDEExecute(idInst, hConv, szCloseAll);

     printf("Closing all PDF documents...\n");

     Sleep(50);    

 } else { // close the specified file

  char cmd_buf[MAX_PATH + 100];

     //[DocOpen(""%P\%N.pdf"")]

     //[DocClose(""%P\%N.pdf"")]  

     printf("Closing %s \n", full_file_name);

     snprintf(cmd_buf, sizeof(cmd_buf), "[DocOpen(\"%s\")]", full_file_name);

     DDEExecute(idInst, hConv, cmd_buf);

     Sleep(50);

     snprintf(cmd_buf, sizeof(cmd_buf), "[DocClose(\"%s\")]", full_file_name);

     DDEExecute(idInst, hConv, cmd_buf);

     Sleep(50);

 }

    //DDE Disconnect and Uninitialize.

    DdeDisconnect(hConv);

    DdeUninitialize(idInst);

   

    if (argc == 1) {

     // close all pdf files

     return (0);

    }

    for (int i = 0; i < 5; ++i) {

     Sleep(100);

     if (!is_file_locked(file_to_close))

      break;

    }

    if (!is_file_locked(file_to_close)) {

     printf("%s is closed.\n", file_to_close);

     return 0;

    } else {

     printf("%s is still open.\n", file_to_close);

     return 1;

    }

}