mirror of
https://github.com/jmarshall23/msword.git
synced 2026-08-11 23:50:53 +02:00
Added unicode support.
This commit is contained in:
+5
-1
@@ -865,7 +865,7 @@ target_compile_features(WORD1 PRIVATE cxx_std_20)
|
||||
target_compile_definitions(WORD1 PRIVATE
|
||||
WIN WIN23 CRLF NONATIVE OPUS_X64 NOMINMAX UNICODE _UNICODE
|
||||
)
|
||||
target_link_libraries(WORD1 PRIVATE opus_original_engine opus_x64_runtime user32 dbghelp)
|
||||
target_link_libraries(WORD1 PRIVATE opus_original_engine opus_x64_runtime user32 imm32 dbghelp)
|
||||
if(MSVC)
|
||||
target_compile_options(WORD1 PRIVATE /J /W3 /wd4005 /utf-8 /guard:cf /sdl)
|
||||
target_link_options(WORD1 PRIVATE /DYNAMICBASE /HIGHENTROPYVA /NXCOMPAT /guard:cf /CETCOMPAT /MANIFEST:NO)
|
||||
@@ -1006,6 +1006,10 @@ add_test(NAME opus_word1_clipboard_shortcut_test
|
||||
COMMAND $<TARGET_FILE:opus_word1_ui_test> $<TARGET_FILE:WORD1> --clipboard
|
||||
)
|
||||
set_tests_properties(opus_word1_clipboard_shortcut_test PROPERTIES TIMEOUT 20)
|
||||
add_test(NAME opus_word1_unicode_test
|
||||
COMMAND $<TARGET_FILE:opus_word1_ui_test> $<TARGET_FILE:WORD1> --unicode
|
||||
)
|
||||
set_tests_properties(opus_word1_unicode_test PROPERTIES TIMEOUT 20)
|
||||
add_test(NAME opus_word1_typing_test
|
||||
COMMAND $<TARGET_FILE:opus_word1_ui_test> $<TARGET_FILE:WORD1> --typing
|
||||
)
|
||||
|
||||
@@ -479,6 +479,7 @@ ChangeClipboard()
|
||||
if (vsab.fOwnClipboard)
|
||||
{ /* only set handles if we really have something in docScrap */
|
||||
SetClipboardData( CF_OWNERDISPLAY, NULL );
|
||||
SetClipboardData( CF_UNICODETEXT, NULL );
|
||||
/* don't put out cf_tiff or other rejected pic types, or
|
||||
rtf or link for any of those types
|
||||
*/
|
||||
|
||||
+49
-3
@@ -70,6 +70,11 @@ extern char szEmpty[];
|
||||
extern struct SCI vsci;
|
||||
extern int vwWinVersion;
|
||||
extern int wwCur;
|
||||
#ifdef OPUS_X64
|
||||
extern HANDLE OpusUnicodeCreateClipboardHandle();
|
||||
extern int OpusUnicodeClipboardToLegacy();
|
||||
extern int OpusUnicodeBindPendingClipboard();
|
||||
#endif
|
||||
|
||||
extern LPCH LpchIncr();
|
||||
|
||||
@@ -279,6 +284,21 @@ int *pfBlankPic; /* set true if an empty picture; else ignored */
|
||||
AssureLegalSel (PcaSet(&ca, doc, cpFirst, cpLim));
|
||||
switch (cf)
|
||||
{
|
||||
case CF_UNICODETEXT:
|
||||
#ifdef OPUS_X64
|
||||
{
|
||||
HANDLE hAnsi;
|
||||
HANDLE hUnicode;
|
||||
hAnsi = HWriteText(ca.doc, ca.cpFirst, ca.cpLim, cbInitial);
|
||||
if (hAnsi == NULL)
|
||||
return NULL;
|
||||
hUnicode = OpusUnicodeCreateClipboardHandle(ca.doc, ca.cpFirst, hAnsi);
|
||||
GlobalFree(hAnsi);
|
||||
return hUnicode;
|
||||
}
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
case CF_TEXT:
|
||||
|
||||
return HWriteText (ca.doc, ca.cpFirst, ca.cpLim, cbInitial);
|
||||
@@ -845,7 +865,7 @@ int FReadExtScrap()
|
||||
{
|
||||
vsab.fMayHavePic = vsab.fPict = fTrue;
|
||||
}
|
||||
if (cf != CF_TEXT)
|
||||
if (cf != CF_TEXT && cf != CF_UNICODETEXT)
|
||||
vsab.fFormatted = fTrue;
|
||||
break;
|
||||
}
|
||||
@@ -884,6 +904,8 @@ int cbInitial;
|
||||
{
|
||||
int docDest;
|
||||
BOOL fOk;
|
||||
BOOL fUnicode = fFalse;
|
||||
HANDLE hConverted = NULL;
|
||||
struct CA ca;
|
||||
|
||||
#ifdef BZ
|
||||
@@ -892,6 +914,18 @@ int cbInitial;
|
||||
|
||||
switch (cf)
|
||||
{
|
||||
case CF_UNICODETEXT:
|
||||
#ifdef OPUS_X64
|
||||
if (!OpusUnicodeClipboardToLegacy(hData, &hConverted) ||
|
||||
hConverted == NULL)
|
||||
return fFalse;
|
||||
hData = hConverted;
|
||||
cf = CF_TEXT;
|
||||
fUnicode = fTrue;
|
||||
break;
|
||||
#else
|
||||
return fFalse;
|
||||
#endif
|
||||
case CF_TEXT:
|
||||
case CF_DIB:
|
||||
case CF_BITMAP:
|
||||
@@ -917,7 +951,11 @@ int cbInitial;
|
||||
docDest = docScrap;
|
||||
/* otherwise use vdocScratch */
|
||||
else if ((docDest = DocCreateScratch (pca->doc)) == docNil)
|
||||
{
|
||||
if (hConverted != NULL)
|
||||
GlobalFree(hConverted);
|
||||
return fFalse;
|
||||
}
|
||||
|
||||
/* empty the destination doc and set styles, fonts to standard */
|
||||
SetWholeDoc (docDest, PcaSetNil(&ca));
|
||||
@@ -939,7 +977,11 @@ int cbInitial;
|
||||
break;
|
||||
case CF_TEXT:
|
||||
fOk = FReadText (docDest, cf, hData, cbInitial);
|
||||
if (fOk && fUnicode)
|
||||
fOk = OpusUnicodeBindPendingClipboard(docDest);
|
||||
}
|
||||
if (hConverted != NULL)
|
||||
GlobalFree(hConverted);
|
||||
|
||||
fOk &= (!vmerr.fMemFail && !vmerr.fDiskFail);
|
||||
|
||||
@@ -1843,7 +1885,8 @@ int cf;
|
||||
BOOL fPrPic;
|
||||
{
|
||||
|
||||
/* Desired order (win3 word: Notice text before picture formats):
|
||||
/* Desired order (Unicode text precedes legacy formats):
|
||||
CF_UNICODETEXT
|
||||
cfRTF
|
||||
CF_TEXT
|
||||
cfPRPic
|
||||
@@ -1876,7 +1919,9 @@ BOOL fPrPic;
|
||||
goto LMetafile; /* 4th or 5th choice */
|
||||
|
||||
case cfNil:
|
||||
return cfRTF; /* 1st choice */
|
||||
return CF_UNICODETEXT; /* 1st choice */
|
||||
case CF_UNICODETEXT:
|
||||
return cfRTF;
|
||||
case CF_TEXT:
|
||||
if (fPrPic && cfPrPic != cfNil)
|
||||
return cfPrPic; /* 4th choice (Excel specific) */
|
||||
@@ -1915,6 +1960,7 @@ BOOL fRestrictRTF;
|
||||
|
||||
switch (cf)
|
||||
{
|
||||
case CF_UNICODETEXT:
|
||||
case CF_TEXT:
|
||||
/* reject text for either a normal pic or an import field pic,
|
||||
since it has no text result and would show nothing */
|
||||
|
||||
@@ -813,6 +813,9 @@ int doc;
|
||||
int isels;
|
||||
struct DOD *pdod, dod;
|
||||
extern struct PL **vhplbmc;
|
||||
#ifdef OPUS_X64
|
||||
extern void OpusUnicodeForgetDocument();
|
||||
#endif
|
||||
|
||||
Assert(doc >= 0 && doc < docMac);
|
||||
Assert(doc == docNil || mpdochdod[doc] != hNil);
|
||||
@@ -848,6 +851,9 @@ int doc;
|
||||
vdocTemp = docNil;
|
||||
else if (doc == vdocScratch)
|
||||
vdocScratch = docNil;
|
||||
#ifdef OPUS_X64
|
||||
OpusUnicodeForgetDocument(doc);
|
||||
#endif
|
||||
DisposeDocPdod(doc, &dod );
|
||||
|
||||
Assert (vdocScratch == docNil || PdodDoc(vdocScratch)->doc
|
||||
|
||||
@@ -73,6 +73,8 @@ BOOL vfNoInval=0; /* can be > 1 */
|
||||
|
||||
#ifdef OPUS_X64
|
||||
struct PLC **HplcCreateEdc();
|
||||
extern void OpusUnicodeOnReplace();
|
||||
extern void OpusUnicodeOnReplaceCps();
|
||||
#else
|
||||
struct PLC *HplcCreateEdc();
|
||||
#endif
|
||||
@@ -463,6 +465,9 @@ struct CA *pca;
|
||||
int fn;
|
||||
FC fc, dfc;
|
||||
{
|
||||
int docUnicode = pca->doc;
|
||||
CP cpFirstUnicode = pca->cpFirst;
|
||||
CP cpLimUnicode = pca->cpLim;
|
||||
struct XBC xbc;
|
||||
struct XSR *pxsr;
|
||||
/* this should be the maximum number of calls to XReplace or XReplaceCps that
|
||||
@@ -489,6 +494,7 @@ should go off) */
|
||||
XReplace(fFalse, &xbc, pxsr);
|
||||
EndCommit();
|
||||
CloseTns(&xbc);
|
||||
OpusUnicodeOnReplace(docUnicode, cpFirstUnicode, cpLimUnicode, dfc);
|
||||
|
||||
return fTrue;
|
||||
}
|
||||
@@ -821,6 +827,12 @@ on failure (in which case document is unchanged) */
|
||||
HANDNATIVE BOOL C_FReplaceCps(pcaDel, pcaIns)
|
||||
struct CA *pcaDel, *pcaIns;
|
||||
{
|
||||
int docDelUnicode = pcaDel->doc;
|
||||
int docInsUnicode = pcaIns->doc;
|
||||
CP cpDelFirstUnicode = pcaDel->cpFirst;
|
||||
CP cpDelLimUnicode = pcaDel->cpLim;
|
||||
CP cpInsFirstUnicode = pcaIns->cpFirst;
|
||||
CP cpInsLimUnicode = pcaIns->cpLim;
|
||||
struct XBC xbc;
|
||||
struct XSR *pxsr;
|
||||
/* this should be the maximum number of calls to XReplace or XReplaceCps that
|
||||
@@ -848,6 +860,9 @@ should go off). Win is greater than Mac because of annotations. */
|
||||
XReplaceCps(fFalse, &xbc, pxsr);
|
||||
EndCommit();
|
||||
CloseTns(&xbc);
|
||||
OpusUnicodeOnReplaceCps(docDelUnicode, cpDelFirstUnicode,
|
||||
cpDelLimUnicode, docInsUnicode, cpInsFirstUnicode,
|
||||
cpInsLimUnicode);
|
||||
|
||||
return fTrue;
|
||||
}
|
||||
|
||||
@@ -1808,6 +1808,7 @@ BOOL fPasteLink;
|
||||
{
|
||||
if ((fPasteLink && fmt == cfLink)
|
||||
|| (!fPasteLink && (fmt == cfRTF ||
|
||||
fmt == CF_UNICODETEXT ||
|
||||
fmt == CF_TEXT ||
|
||||
fmt == CF_BITMAP ||
|
||||
fmt == CF_METAFILEPICT)))
|
||||
|
||||
+24
-7
@@ -128,6 +128,10 @@ extern struct DBS vdbs;
|
||||
|
||||
extern struct PREF vpref;
|
||||
extern struct PRI vpri;
|
||||
#ifdef OPUS_X64
|
||||
extern int OpusUnicodeExtTextOut();
|
||||
extern int OpusUnicodeHasRange();
|
||||
#endif
|
||||
|
||||
extern int vlm;
|
||||
extern int vfPrvwDisp;
|
||||
@@ -1256,13 +1260,26 @@ LDacNop:
|
||||
else
|
||||
{
|
||||
LRealExtTextOut:
|
||||
ExtTextOut(hdc, xp, yp,
|
||||
eto, /* action bits */
|
||||
fRcAdjusted ? (LPRECT)&rcAdjusted :
|
||||
(LPRECT)&rcOpaque, /* opaque rect */
|
||||
(LPCH)&vfli.rgch [ich], /* string */
|
||||
cch, /* length */
|
||||
lpdxp ); /* lpdx */
|
||||
#ifdef OPUS_X64
|
||||
ExtTextOut(hdc, xp, yp, eto,
|
||||
fRcAdjusted ? (LPRECT)&rcAdjusted :
|
||||
(LPRECT)&rcOpaque,
|
||||
(LPCH)&vfli.rgch [ich], cch, lpdxp);
|
||||
if (OpusUnicodeHasRange(vfli.doc,
|
||||
vfli.cpMin + dcpVanish + ich, cch))
|
||||
OpusUnicodeExtTextOut(hdc, xp, yp, eto,
|
||||
fRcAdjusted ? (LPRECT)&rcAdjusted :
|
||||
(LPRECT)&rcOpaque,
|
||||
vfli.doc,
|
||||
vfli.cpMin + dcpVanish + ich,
|
||||
(LPCH)&vfli.rgch [ich], cch, lpdxp);
|
||||
#else
|
||||
ExtTextOut(hdc, xp, yp,
|
||||
eto,
|
||||
fRcAdjusted ? (LPRECT)&rcAdjusted :
|
||||
(LPRECT)&rcOpaque,
|
||||
(LPCH)&vfli.rgch [ich], cch, lpdxp);
|
||||
#endif
|
||||
}
|
||||
vrf.fInExternalCall = fFalse;
|
||||
(char *) pchr = (char *)*vhgrpchr + bchrCur;
|
||||
|
||||
@@ -1265,6 +1265,15 @@ LHaveEvent:
|
||||
return fFalse;
|
||||
}
|
||||
|
||||
#ifdef OPUS_X64
|
||||
/* End the legacy one-byte insertion batch before a UTF-16 WM_CHAR is
|
||||
consumed. The outer message loop will pass it to the Unicode bridge
|
||||
with the full code unit intact. */
|
||||
if (vmsgLast.message == WM_CHAR &&
|
||||
(unsigned int)vmsgLast.wParam >= 0x100)
|
||||
return fFalse;
|
||||
#endif
|
||||
|
||||
if (FIsKeyMessage((LPMSG) &vmsgLast))
|
||||
{
|
||||
if (vmsgLast.wParam & 0x8000) /* indicates we did xlation */
|
||||
|
||||
+7
-3
@@ -91,6 +91,7 @@ extern int OpusModernPendingDocxParagraphCount();
|
||||
extern int OpusModernGetPendingDocxRun();
|
||||
extern int OpusModernGetPendingDocxParagraph();
|
||||
extern int OpusModernGetPendingDocxPage();
|
||||
extern int OpusModernBindPendingDocxUnicode();
|
||||
extern void OpusModernClearPendingDocxFormatting();
|
||||
extern int OpusX64FtcFromFontNameForDoc();
|
||||
#endif
|
||||
@@ -128,8 +129,9 @@ int doc;
|
||||
struct CA ca;
|
||||
char grpprl[96];
|
||||
char szFont[LF_FACESIZE];
|
||||
char szLanguage[32];
|
||||
int bold, italic, underline, strike, smallCaps, allCaps, hidden;
|
||||
int hps, ico;
|
||||
int hps, ico, charset;
|
||||
int jc, dxaLeft, dxaRight, dxaLeft1, dyaBefore, dyaAfter, dyaLine;
|
||||
int fKeep, fKeepFollow, fPageBreakBefore, fBottomBorder;
|
||||
int xaPage, yaPage, dxaLeftPage, dxaRightPage, dyaTopPage, dyaBottomPage;
|
||||
@@ -195,7 +197,8 @@ int doc;
|
||||
{
|
||||
if (!OpusModernGetPendingDocxRun(i, &cpFirst, &cpLim, &bold,
|
||||
&italic, &underline, &strike, &smallCaps, &allCaps,
|
||||
&hidden, &hps, &ico, szFont, sizeof(szFont)))
|
||||
&hidden, &hps, &ico, szFont, sizeof(szFont), &charset,
|
||||
szLanguage, sizeof(szLanguage)))
|
||||
continue;
|
||||
if (cpFirst < cp0 || cpFirst >= cpMac)
|
||||
continue;
|
||||
@@ -214,7 +217,7 @@ int doc;
|
||||
cb = CbAppendDocxPrl(grpprl, cb, sizeof(grpprl), sprmCFVanish, hidden);
|
||||
cb = CbAppendDocxPrl(grpprl, cb, sizeof(grpprl), sprmCHps, hps);
|
||||
cb = CbAppendDocxPrl(grpprl, cb, sizeof(grpprl), sprmCIco, ico);
|
||||
ftc = OpusX64FtcFromFontNameForDoc(doc, szFont);
|
||||
ftc = OpusX64FtcFromFontNameForDoc(doc, szFont, charset);
|
||||
if (ftc >= 0)
|
||||
cb = CbAppendDocxPrl(grpprl, cb, sizeof(grpprl), sprmCFtc, ftc);
|
||||
ca.doc = doc;
|
||||
@@ -224,6 +227,7 @@ int doc;
|
||||
}
|
||||
|
||||
PdodDoc(doc)->fFormatted = fTrue;
|
||||
OpusModernBindPendingDocxUnicode(doc);
|
||||
OpusModernClearPendingDocxFormatting();
|
||||
}
|
||||
#endif
|
||||
|
||||
+69
-12
@@ -105,6 +105,24 @@ static int vWin95ZoomPercent = 100;
|
||||
static int vWin95BaseDxsInch = 0;
|
||||
static int vWin95BaseDysInch = 0;
|
||||
int vOpusPdfExportStage = 0;
|
||||
static int vOpusDocxSnapshotDoc = docNil;
|
||||
static const char *vszOpusDocxSnapshotPath = NULL;
|
||||
|
||||
int OpusGetUnicodeSelection(pdoc, pcpFirst, pcpLim)
|
||||
int *pdoc;
|
||||
long *pcpFirst;
|
||||
long *pcpLim;
|
||||
{
|
||||
if (selCur.doc == docNil)
|
||||
return fFalse;
|
||||
if (pdoc != NULL)
|
||||
*pdoc = selCur.doc;
|
||||
if (pcpFirst != NULL)
|
||||
*pcpFirst = selCur.cpFirst;
|
||||
if (pcpLim != NULL)
|
||||
*pcpLim = selCur.cpLim;
|
||||
return fTrue;
|
||||
}
|
||||
|
||||
int OpusExportCurrentDocumentPdf()
|
||||
{
|
||||
@@ -117,14 +135,19 @@ int OpusExportCurrentDocumentPdf()
|
||||
extern CHAR HUGE *vhpchFetch;
|
||||
extern int OpusPdfSnapshotBegin();
|
||||
extern int OpusPdfSnapshotAddParagraph();
|
||||
extern int OpusPdfSnapshotAddRun();
|
||||
extern int OpusPdfSnapshotAddRunUtf8();
|
||||
extern int OpusPdfSnapshotExportDialog();
|
||||
extern int OpusDocxSnapshotExportPath();
|
||||
extern void OpusX64FontNameFromFtc();
|
||||
extern int OpusX64CharsetFromFtc();
|
||||
extern int OpusUnicodeTextToUtf8();
|
||||
extern int OpusUnicodeLanguageTagAt();
|
||||
|
||||
vOpusPdfExportStage = 1;
|
||||
if (selCur.doc == docNil)
|
||||
if (vOpusDocxSnapshotDoc == docNil && selCur.doc == docNil)
|
||||
return fFalse;
|
||||
doc = DocMother(selCur.doc);
|
||||
doc = vOpusDocxSnapshotDoc != docNil ? vOpusDocxSnapshotDoc :
|
||||
DocMother(selCur.doc);
|
||||
cpMac = CpMacDocEdit(doc);
|
||||
if (cpMac < cp0 || cpMac > 0x3fffffffL)
|
||||
return fFalse;
|
||||
@@ -159,43 +182,59 @@ int OpusExportCurrentDocumentPdf()
|
||||
int ich;
|
||||
int cch;
|
||||
int cchOutput;
|
||||
char rgch[1024];
|
||||
char rgch[4096];
|
||||
char szFont[LF_FACESIZE];
|
||||
char szLanguage[32];
|
||||
int charset;
|
||||
FetchCp(doc, cpRun, fcmChars + fcmProps);
|
||||
if (vccpFetch <= 0 || vhpchFetch == NULL)
|
||||
return fFalse;
|
||||
cch = (int)CpMin((CP)vccpFetch, cpParaLim - cpRun);
|
||||
cch = min(cch, sizeof(rgch) - 1);
|
||||
cch = min(cch, (sizeof(rgch) - 1) / 4);
|
||||
charset = OpusX64CharsetFromFtc(doc, vchpFetch.ftc);
|
||||
cchOutput = 0;
|
||||
for (ich = 0; ich < cch; ++ich)
|
||||
{
|
||||
int ch = (unsigned char)vhpchFetch[ich];
|
||||
char chOutput;
|
||||
char rgchUtf8[8];
|
||||
int cchUtf8;
|
||||
if (ch == chReturn || ch == chEop)
|
||||
continue;
|
||||
if (ch == chTable || ch == chTab)
|
||||
rgch[cchOutput++] = '\t';
|
||||
chOutput = '\t';
|
||||
else if (ch == chSect || ch == chColumnBreak)
|
||||
rgch[cchOutput++] = '\f';
|
||||
chOutput = '\f';
|
||||
else if (ch == chNonReqHyphen)
|
||||
rgch[cchOutput++] = '-';
|
||||
chOutput = '-';
|
||||
/* fSpec can cover imported field/list runs whose bytes are still
|
||||
ordinary visible text. Filter actual control codes above, not
|
||||
the whole run, or leading words disappear from PDF output. */
|
||||
else if (ch >= chSpace)
|
||||
rgch[cchOutput++] = (char)ch;
|
||||
chOutput = (char)ch;
|
||||
else
|
||||
continue;
|
||||
cchUtf8 = OpusUnicodeTextToUtf8(doc, cpRun + ich,
|
||||
&chOutput, 1, charset, rgchUtf8, sizeof(rgchUtf8));
|
||||
if (cchUtf8 < 0 || cchUtf8 > sizeof(rgch) - 1 - cchOutput)
|
||||
return fFalse;
|
||||
bltbyte(rgchUtf8, rgch + cchOutput, cchUtf8);
|
||||
cchOutput += cchUtf8;
|
||||
}
|
||||
if (cchOutput > 0)
|
||||
{
|
||||
rgch[cchOutput] = '\0';
|
||||
OpusX64FontNameFromFtc(vchpFetch.ftc,
|
||||
szFont, sizeof(szFont));
|
||||
OpusUnicodeLanguageTagAt(doc, cpRun,
|
||||
szLanguage, sizeof(szLanguage));
|
||||
vOpusPdfExportStage = 100000 + (int)cpRun;
|
||||
if (!OpusPdfSnapshotAddRun(rgch, cchOutput, szFont,
|
||||
if (!OpusPdfSnapshotAddRunUtf8(rgch, cchOutput, szFont,
|
||||
vchpFetch.hps, vchpFetch.fBold,
|
||||
vchpFetch.fItalic, vchpFetch.kul != kulNone,
|
||||
vchpFetch.fStrike, vchpFetch.fSmallCaps,
|
||||
vchpFetch.fCaps, vchpFetch.fVanish,
|
||||
vchpFetch.ico))
|
||||
vchpFetch.ico, szLanguage, charset))
|
||||
return fFalse;
|
||||
}
|
||||
cpRun += cch;
|
||||
@@ -203,11 +242,29 @@ int OpusExportCurrentDocumentPdf()
|
||||
cpPara = cpParaLim;
|
||||
}
|
||||
vOpusPdfExportStage = 3;
|
||||
result = OpusPdfSnapshotExportDialog(vhwndApp);
|
||||
result = vszOpusDocxSnapshotPath != NULL ?
|
||||
OpusDocxSnapshotExportPath(vszOpusDocxSnapshotPath) :
|
||||
OpusPdfSnapshotExportDialog(vhwndApp);
|
||||
vOpusPdfExportStage = result ? 4 : -4;
|
||||
return result;
|
||||
}
|
||||
|
||||
int OpusSaveDocumentAsDocx(doc, szPath)
|
||||
int doc;
|
||||
const char *szPath;
|
||||
{
|
||||
int result;
|
||||
if (doc == docNil || szPath == NULL || *szPath == '\0' ||
|
||||
vOpusDocxSnapshotDoc != docNil)
|
||||
return fFalse;
|
||||
vOpusDocxSnapshotDoc = DocMother(doc);
|
||||
vszOpusDocxSnapshotPath = szPath;
|
||||
result = OpusExportCurrentDocumentPdf();
|
||||
vOpusDocxSnapshotDoc = docNil;
|
||||
vszOpusDocxSnapshotPath = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* The Word 1.x Color command opens a keyboard prompt. The Win95 chrome
|
||||
supplies a palette instead, but still applies the original character
|
||||
property so document storage and formatting behavior remain unchanged. */
|
||||
|
||||
+1
-1
@@ -708,7 +708,7 @@ LCleanup:
|
||||
FWin95SaveAliasMatches(stFile) && PdodDoc(doc)->fn != fnNil)
|
||||
FCloseFn(PdodDoc(doc)->fn);
|
||||
if (!OpusFinishWin95SaveAlias(stFile,
|
||||
cmd == cmdOK && pcmb->fAction) && cmd == cmdOK)
|
||||
cmd == cmdOK && pcmb->fAction, doc) && cmd == cmdOK)
|
||||
cmd = cmdError;
|
||||
#endif
|
||||
if (cmd == cmdCancelled)
|
||||
|
||||
+53
-14
@@ -1002,27 +1002,50 @@ const char *sz;
|
||||
/* Variant used while a DOCX is being opened. The new document has not yet
|
||||
become selCur.doc, so the normal ribbon adapter would add the font to the
|
||||
document that was active before File Open. */
|
||||
EXPORT int OpusX64FtcFromFontNameForDoc(doc, sz)
|
||||
EXPORT int OpusX64FtcFromFontNameForDoc(doc, sz, charset)
|
||||
int doc;
|
||||
const char *sz;
|
||||
int charset;
|
||||
{
|
||||
int cch;
|
||||
struct FFN *pffn;
|
||||
char rgch[cbFfnLast] = {0};
|
||||
|
||||
if (sz == NULL || *sz == '\0')
|
||||
return wNinch;
|
||||
/* Word 1.x keeps only three guaranteed GDI font slots in a new plain-text
|
||||
document: Times, Symbol, and Helvetica. Adding Office-era fonts before
|
||||
the imported document becomes current can alias the new ftc to Symbol.
|
||||
Preserve the requested family safely; Helvetica is the closest available
|
||||
substitute for Aptos/Calibri/Arial and retains ANSI character mapping. */
|
||||
/* A new Word 1.x document exposes only the three guaranteed screen-font
|
||||
slots while DOCX formatting is applied. Adding a normal ANSI font at
|
||||
this point can alias it to slot 1 (Symbol), corrupting every Latin
|
||||
character in the document. Keep the historical safe mappings for ANSI
|
||||
faces; charset-specific runs still get real FFN records below. */
|
||||
if (!FNeNcSz(sz, "Symbol") || !FNeNcSz(sz, "Wingdings"))
|
||||
return 1;
|
||||
if (!FNeNcSz(sz, "Times New Roman") || !FNeNcSz(sz, "Times") ||
|
||||
!FNeNcSz(sz, "Georgia") || !FNeNcSz(sz, "Garamond") ||
|
||||
!FNeNcSz(sz, "Cambria"))
|
||||
return ftcDefault;
|
||||
if (!FNeNcSz(sz, "Courier") || !FNeNcSz(sz, "Courier New") ||
|
||||
!FNeNcSz(sz, "Consolas"))
|
||||
return FtcFromDocIbst(doc, ibstCourier);
|
||||
return 2;
|
||||
if (charset == ANSI_CHARSET || charset == DEFAULT_CHARSET)
|
||||
{
|
||||
if (!FNeNcSz(sz, "Times New Roman") || !FNeNcSz(sz, "Times") ||
|
||||
!FNeNcSz(sz, "Georgia") || !FNeNcSz(sz, "Garamond") ||
|
||||
!FNeNcSz(sz, "Cambria"))
|
||||
return ftcDefault;
|
||||
if (!FNeNcSz(sz, "Courier") || !FNeNcSz(sz, "Courier New") ||
|
||||
!FNeNcSz(sz, "Consolas"))
|
||||
return FtcFromDocIbst(doc, ibstCourier);
|
||||
return 2;
|
||||
}
|
||||
pffn = (struct FFN *)rgch;
|
||||
if (CchSz(sz) > LF_FACESIZE - 1)
|
||||
{
|
||||
bltbyte(sz, pffn->szFfn, LF_FACESIZE - 1);
|
||||
pffn->szFfn[LF_FACESIZE - 1] = '\0';
|
||||
}
|
||||
else
|
||||
CchCopySz(sz, pffn->szFfn);
|
||||
cch = CchStripString(pffn->szFfn, CchSz(pffn->szFfn) - 1) + 1;
|
||||
if (cch <= 1)
|
||||
return wNinch;
|
||||
pffn->ffid = FF_DONTCARE;
|
||||
pffn->cbFfnM1 = CbFfnFromCchSzFfn(cch) - 1;
|
||||
ChsPffn(pffn) = (charset >= 0 && charset <= 255) ? charset : ANSI_CHARSET;
|
||||
return FtcChkDocFfn(doc, pffn);
|
||||
}
|
||||
|
||||
EXPORT int OpusX64HpsFromFontSize(sz)
|
||||
@@ -1067,6 +1090,22 @@ int cchMax;
|
||||
bltbyte(szFont, sz, min(CchSz(szFont), cchMax));
|
||||
sz[cchMax - 1] = '\0';
|
||||
}
|
||||
|
||||
EXPORT int OpusX64CharsetFromFtc(doc, ftc)
|
||||
int doc;
|
||||
int ftc;
|
||||
{
|
||||
int ibst;
|
||||
struct FFN *pffn;
|
||||
|
||||
if (doc == docNil || ftc == wNinch)
|
||||
return ANSI_CHARSET;
|
||||
ibst = IbstFontFromFtcDoc(ftc, doc);
|
||||
if (ibst == iNil)
|
||||
return ANSI_CHARSET;
|
||||
pffn = (struct FFN *)PstFromSttb(vhsttbFont, ibst);
|
||||
return (unsigned char)ChsPffn(pffn);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -96,6 +96,8 @@ extern struct FTI vfti;
|
||||
extern char (**vhgrpchr)[];
|
||||
extern int vbchrMac;
|
||||
extern struct STTB **vhsttbFont;
|
||||
extern unsigned int OpusUnicodeScalarAt();
|
||||
extern int OpusQueueUnicodeWmChar();
|
||||
#endif
|
||||
extern struct FCB **mpfnhfcb[];
|
||||
extern int vfnPreload;
|
||||
@@ -1834,6 +1836,12 @@ LONG lParam;
|
||||
extern int vOpusPdfExportStage;
|
||||
return (LRESULT)vOpusPdfExportStage;
|
||||
}
|
||||
case 106:
|
||||
return (LRESULT)OpusUnicodeScalarAt(
|
||||
selCur.doc, (CP)lParam);
|
||||
case 107:
|
||||
return (LRESULT)OpusQueueUnicodeWmChar(
|
||||
hwnd, (unsigned int)lParam);
|
||||
}
|
||||
return (LRESULT) -1;
|
||||
#endif
|
||||
@@ -2501,6 +2509,17 @@ LPMSG lpmsg;
|
||||
wm = lpmsg->message;
|
||||
kc = lpmsg->wParam; /* key code or char */
|
||||
|
||||
#ifdef OPUS_X64
|
||||
/* Unicode window classes deliver non-ANSI keyboard layouts as UTF-16
|
||||
WM_CHAR units. Queue those through the compatibility layer before the
|
||||
legacy insertion loop truncates the character to one byte. */
|
||||
if (wm == WM_CHAR && !vfInsertMode && (unsigned int)kc >= 0x100)
|
||||
{
|
||||
OpusQueueUnicodeWmChar(lpmsg->hwnd, (unsigned int)kc);
|
||||
return fTrue;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef COMING_SOON_TO_A_WORD_PROCESSOR_NEAR_YOU
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,6 +11,8 @@ extern "C" int OpusModernDocxToRtfFile(const char*, const char*);
|
||||
extern "C" int OpusModernDocxToTextFile(const char*, const char*);
|
||||
extern "C" int OpusModernRtfFileToDocx(const char*, const char*);
|
||||
extern "C" int OpusModernRtfFileToPdf(const char*, const char*);
|
||||
extern "C" int OpusModernBindPendingDocxUnicode(int);
|
||||
extern "C" unsigned int OpusUnicodeScalarAt(int, long);
|
||||
extern "C" int OpusPdfSnapshotBegin(int, int, int, int, int, int);
|
||||
extern "C" int OpusPdfSnapshotAddParagraph(int, int, int, int, int, int,
|
||||
int, int, int, int, int);
|
||||
@@ -52,6 +54,7 @@ int main(const int argument_count, char** arguments) {
|
||||
const std::string rtf = base + ".rtf";
|
||||
const std::string docx = base + ".docx";
|
||||
const std::string roundtrip = base + ".roundtrip.rtf";
|
||||
const std::string imported_text = base + ".unicode.txt";
|
||||
const std::string oversized = base + ".oversized.rtf";
|
||||
const std::string preserved = base + ".preserved.pdf";
|
||||
char requested_pdf[32768]{};
|
||||
@@ -67,11 +70,33 @@ int main(const int argument_count, char** arguments) {
|
||||
output << "{\\rtf1\\ansi{\\fonttbl{\\f0 Arial;}}"
|
||||
"{\\colortbl;\\red255\\green0\\blue0;}"
|
||||
"\\f0\\fs24 Plain {\\b Bold} {\\i Italic} "
|
||||
"{\\ul Underline} {\\cf1 Red}\\par Second paragraph}";
|
||||
"{\\ul Underline} {\\cf1 Red} "
|
||||
"{\\lang1049 \\u1055?\\u1088?\\u1080?\\u1074?\\u1077?\\u1090?}"
|
||||
" {\\lang1032 \\u915?\\u949?\\u953?\\u940?}"
|
||||
" {\\lang1025 \\u1605?\\u1585?\\u1581?\\u1576?\\u1575?}"
|
||||
" {\\lang1041 \\u12371?\\u12435?\\u12395?\\u12385?\\u12399?}"
|
||||
"\\par Second paragraph}";
|
||||
}
|
||||
const bool written = OpusModernRtfFileToDocx(rtf.c_str(), docx.c_str()) != 0;
|
||||
const bool read = written &&
|
||||
OpusModernDocxToRtfFile(docx.c_str(), roundtrip.c_str()) != 0;
|
||||
const bool unicode_imported = written &&
|
||||
OpusModernDocxToTextFile(docx.c_str(), imported_text.c_str()) != 0 &&
|
||||
OpusModernBindPendingDocxUnicode(42) != 0;
|
||||
bool cyrillic_sidecar = false;
|
||||
bool greek_sidecar = false;
|
||||
bool arabic_sidecar = false;
|
||||
bool japanese_sidecar = false;
|
||||
if (unicode_imported) {
|
||||
const std::string imported_bytes = read_file(imported_text);
|
||||
for (long cp = 0; cp < static_cast<long>(imported_bytes.size()); ++cp) {
|
||||
const unsigned int scalar = OpusUnicodeScalarAt(42, cp);
|
||||
if (scalar == 1055) cyrillic_sidecar = true;
|
||||
if (scalar == 915) greek_sidecar = true;
|
||||
if (scalar == 1605) arabic_sidecar = true;
|
||||
if (scalar == 12371) japanese_sidecar = true;
|
||||
}
|
||||
}
|
||||
const bool pdf_written = OpusModernRtfFileToPdf(rtf.c_str(), pdf.c_str()) != 0;
|
||||
{
|
||||
std::ofstream output(preserved, std::ios::binary);
|
||||
@@ -115,17 +140,26 @@ int main(const int argument_count, char** arguments) {
|
||||
DeleteFileA(rtf.c_str());
|
||||
DeleteFileA(docx.c_str());
|
||||
DeleteFileA(roundtrip.c_str());
|
||||
DeleteFileA(imported_text.c_str());
|
||||
DeleteFileA(oversized.c_str());
|
||||
DeleteFileA(preserved.c_str());
|
||||
if (!keep_pdf) DeleteFileA(pdf.c_str());
|
||||
if (!written || !read || result.find("Bold") == std::string::npos ||
|
||||
if (!written || !read || !unicode_imported || !cyrillic_sidecar ||
|
||||
!greek_sidecar || !arabic_sidecar || !japanese_sidecar ||
|
||||
result.find("Bold") == std::string::npos ||
|
||||
result.find("Second paragraph") == std::string::npos ||
|
||||
result.find("\\u1055") == std::string::npos ||
|
||||
result.find("\\u915") == std::string::npos ||
|
||||
result.find("\\u1605") == std::string::npos ||
|
||||
result.find("\\u12371") == std::string::npos ||
|
||||
result.find("\\b") == std::string::npos ||
|
||||
result.find("\\cf1") == std::string::npos || !pdf_written ||
|
||||
!pdf_data.starts_with("%PDF-") ||
|
||||
pdf_data.find("Plain") == std::string::npos ||
|
||||
pdf_data.find("Bold") == std::string::npos ||
|
||||
pdf_data.find("Second paragraph") == std::string::npos ||
|
||||
pdf_data.find("/Encoding /Identity-H") == std::string::npos ||
|
||||
pdf_data.find("/ToUnicode") == std::string::npos ||
|
||||
pdf_data.find("/Helvetica-Bold") == std::string::npos ||
|
||||
pdf_data.find("xref") == std::string::npos ||
|
||||
pdf_data.find("%%EOF") == std::string::npos ||
|
||||
|
||||
@@ -26,6 +26,7 @@ int FSetCabSz(void**, const char*, std::uint16_t);
|
||||
int OpusModernPathIsDocx(const char*);
|
||||
int OpusModernDocxToTextFile(const char*, const char*);
|
||||
int OpusModernRtfFileToDocx(const char*, const char*);
|
||||
int OpusSaveDocumentAsDocx(int, const char*);
|
||||
}
|
||||
|
||||
extern "C" void OpusX64TraceRibbon(const char*, int, int, int, int,
|
||||
@@ -2527,7 +2528,7 @@ int OpusWin95DisplayAlias(unsigned char* const st_file) {
|
||||
}
|
||||
|
||||
int OpusFinishWin95SaveAlias(const unsigned char* st_file,
|
||||
const int success) {
|
||||
const int success, const int doc) {
|
||||
const std::string path = counted_path(st_file);
|
||||
if (path.empty()) {
|
||||
return !g_win95_save_alias.active;
|
||||
@@ -2541,9 +2542,8 @@ int OpusFinishWin95SaveAlias(const unsigned char* st_file,
|
||||
if (copied) {
|
||||
copied = OpusModernPathIsDocx(
|
||||
g_win95_save_alias.selected_path.c_str()) ?
|
||||
OpusModernRtfFileToDocx(
|
||||
g_win95_save_alias.legacy_path.c_str(),
|
||||
g_win95_save_alias.selected_path.c_str()) != 0 :
|
||||
OpusSaveDocumentAsDocx(
|
||||
doc, g_win95_save_alias.selected_path.c_str()) != 0 :
|
||||
atomic_copy_file(g_win95_save_alias.legacy_path,
|
||||
g_win95_save_alias.selected_path);
|
||||
}
|
||||
@@ -2565,7 +2565,7 @@ int OpusFinishWin95SaveAlias(const unsigned char* st_file,
|
||||
return false;
|
||||
}
|
||||
return OpusModernPathIsDocx(saved->second.c_str()) ?
|
||||
OpusModernRtfFileToDocx(path.c_str(), saved->second.c_str()) != 0 :
|
||||
OpusSaveDocumentAsDocx(doc, saved->second.c_str()) != 0 :
|
||||
atomic_copy_file(path, saved->second);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <windowsx.h>
|
||||
#include <imm.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -36,6 +38,14 @@ extern "C" int OpusAdjustWin95HorizontalMargin(
|
||||
HWND ruler, int left_margin, int delta_pixels);
|
||||
extern "C" void OpusDrawWin95HorizontalRuler(HWND ruler);
|
||||
extern "C" int OpusExportCurrentDocumentPdf(void);
|
||||
extern "C" int OpusGetUnicodeSelection(
|
||||
int* doc, long* cp_first, long* cp_lim);
|
||||
extern "C" int OpusUnicodeLegacyByteForScalar(unsigned int scalar);
|
||||
extern "C" int OpusUnicodeSetScalar(
|
||||
int doc, long cp, unsigned int scalar);
|
||||
extern "C" int OpusUnicodeSetInputLanguage(const char* language);
|
||||
extern "C" int OpusUnicodeGetInputLanguage(char* language, int capacity);
|
||||
extern "C" int OpusQueueUnicodeWmChar(HWND pane, unsigned int code_unit);
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -57,7 +67,10 @@ constexpr UINT kCmdToggleStandardToolbar = 0x7101;
|
||||
constexpr UINT kCmdToggleFormattingToolbar = 0x7102;
|
||||
constexpr UINT kCmdExportPdf = 0x7103;
|
||||
constexpr UINT kCmdTextColorBase = 0x7200;
|
||||
constexpr UINT kCmdLanguageBase = 0x7300;
|
||||
constexpr UINT_PTR kSyncTimer = 0x951;
|
||||
constexpr UINT kWmCommitUnicodeScalar = WM_APP + 0x452;
|
||||
constexpr std::size_t kMaxPendingUnicodeInput = 0xffff;
|
||||
constexpr wchar_t kOriginalPaneProcProperty[] = L"OpusWord95OriginalPaneProc";
|
||||
|
||||
enum class FormatGlyph {
|
||||
@@ -168,8 +181,47 @@ struct ToolbarState {
|
||||
HBRUSH g_menu_brush = nullptr;
|
||||
HMENU g_table_menu = nullptr;
|
||||
HMENU g_toolbars_menu = nullptr;
|
||||
HMENU g_language_menu = nullptr;
|
||||
WNDPROC g_original_app_proc = nullptr;
|
||||
bool g_word95_page_view_active = false;
|
||||
wchar_t g_pending_high_surrogate = 0;
|
||||
|
||||
struct LanguageChoice {
|
||||
UINT command;
|
||||
const wchar_t* label;
|
||||
const char* tag;
|
||||
};
|
||||
|
||||
struct PendingUnicodeInput {
|
||||
int doc = -1;
|
||||
long cp = 0;
|
||||
std::uint32_t scalar = 0;
|
||||
int retries = 0;
|
||||
};
|
||||
|
||||
std::deque<PendingUnicodeInput> g_pending_unicode_inputs;
|
||||
PendingUnicodeInput g_active_unicode_input;
|
||||
bool g_unicode_input_active = false;
|
||||
|
||||
constexpr std::array<LanguageChoice, 17> kLanguageChoices{{
|
||||
{kCmdLanguageBase, L"&Automatic (Keyboard)", "auto"},
|
||||
{kCmdLanguageBase + 1, L"&English (United States)", "en-US"},
|
||||
{kCmdLanguageBase + 2, L"&Spanish", "es-ES"},
|
||||
{kCmdLanguageBase + 3, L"&French", "fr-FR"},
|
||||
{kCmdLanguageBase + 4, L"&German", "de-DE"},
|
||||
{kCmdLanguageBase + 5, L"&Polish / Central European", "pl-PL"},
|
||||
{kCmdLanguageBase + 6, L"&Greek", "el-GR"},
|
||||
{kCmdLanguageBase + 7, L"&Russian / Cyrillic", "ru-RU"},
|
||||
{kCmdLanguageBase + 8, L"&Turkish", "tr-TR"},
|
||||
{kCmdLanguageBase + 9, L"&Hebrew", "he-IL"},
|
||||
{kCmdLanguageBase + 10, L"&Arabic", "ar-SA"},
|
||||
{kCmdLanguageBase + 11, L"&Thai", "th-TH"},
|
||||
{kCmdLanguageBase + 12, L"&Vietnamese", "vi-VN"},
|
||||
{kCmdLanguageBase + 13, L"&Japanese", "ja-JP"},
|
||||
{kCmdLanguageBase + 14, L"Chinese (&Simplified)", "zh-CN"},
|
||||
{kCmdLanguageBase + 15, L"Chinese (&Traditional)", "zh-TW"},
|
||||
{kCmdLanguageBase + 16, L"&Korean", "ko-KR"},
|
||||
}};
|
||||
|
||||
struct VerticalRulerDragState {
|
||||
HWND pane = nullptr;
|
||||
@@ -347,6 +399,33 @@ void configure_word95_menus(HWND window) {
|
||||
MF_BYPOSITION | MF_POPUP | MF_STRING,
|
||||
reinterpret_cast<UINT_PTR>(tools), L"&Tools");
|
||||
}
|
||||
const int tools_index = find_named(L"Tools");
|
||||
HMENU tools_menu = tools_index >= 0 ? GetSubMenu(root, tools_index) :
|
||||
(utilities_index >= 0 ? GetSubMenu(root, utilities_index) : nullptr);
|
||||
if (g_language_menu == nullptr || !IsMenu(g_language_menu)) {
|
||||
g_language_menu = CreatePopupMenu();
|
||||
if (g_language_menu != nullptr) {
|
||||
for (const LanguageChoice& choice : kLanguageChoices) {
|
||||
AppendMenuW(g_language_menu, MF_STRING, choice.command,
|
||||
choice.label);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tools_menu != nullptr && g_language_menu != nullptr) {
|
||||
bool present = false;
|
||||
for (int index = 0; index < GetMenuItemCount(tools_menu); ++index) {
|
||||
if (GetSubMenu(tools_menu, index) == g_language_menu) {
|
||||
present = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!present) {
|
||||
AppendMenuW(tools_menu, MF_SEPARATOR, 0, nullptr);
|
||||
AppendMenuW(tools_menu, MF_POPUP | MF_STRING,
|
||||
reinterpret_cast<UINT_PTR>(g_language_menu),
|
||||
L"&Language");
|
||||
}
|
||||
}
|
||||
|
||||
if (g_table_menu == nullptr || !IsMenu(g_table_menu)) {
|
||||
g_table_menu = CreatePopupMenu();
|
||||
@@ -2313,9 +2392,153 @@ void show_document_context_menu(HWND pane, POINT screen_point) {
|
||||
}
|
||||
}
|
||||
|
||||
void update_language_menu_check() {
|
||||
if (g_language_menu == nullptr) return;
|
||||
char selected[32]{};
|
||||
OpusUnicodeGetInputLanguage(selected, static_cast<int>(std::size(selected)));
|
||||
UINT command = kCmdLanguageBase;
|
||||
for (const LanguageChoice& choice : kLanguageChoices) {
|
||||
if (_stricmp(selected, choice.tag) == 0) {
|
||||
command = choice.command;
|
||||
break;
|
||||
}
|
||||
}
|
||||
CheckMenuRadioItem(g_language_menu, kCmdLanguageBase,
|
||||
kCmdLanguageBase +
|
||||
static_cast<UINT>(kLanguageChoices.size() - 1),
|
||||
command, MF_BYCOMMAND);
|
||||
}
|
||||
|
||||
bool start_next_unicode_input(HWND pane) {
|
||||
if (g_unicode_input_active || g_pending_unicode_inputs.empty())
|
||||
return true;
|
||||
int doc_before = -1;
|
||||
long cp_before = 0;
|
||||
long cp_lim_before = 0;
|
||||
if (!OpusGetUnicodeSelection(&doc_before, &cp_before, &cp_lim_before)) {
|
||||
g_pending_unicode_inputs.pop_front();
|
||||
return false;
|
||||
}
|
||||
g_active_unicode_input = g_pending_unicode_inputs.front();
|
||||
g_pending_unicode_inputs.pop_front();
|
||||
g_active_unicode_input.doc = doc_before;
|
||||
g_active_unicode_input.cp = cp_before;
|
||||
g_active_unicode_input.retries = 0;
|
||||
g_unicode_input_active = true;
|
||||
const WPARAM legacy = g_active_unicode_input.scalar < 0x20 ?
|
||||
static_cast<WPARAM>(g_active_unicode_input.scalar) :
|
||||
static_cast<WPARAM>(OpusUnicodeLegacyByteForScalar(
|
||||
g_active_unicode_input.scalar) & 0xff);
|
||||
if (!PostMessageW(pane, WM_CHAR, legacy, 1) ||
|
||||
!PostMessageW(pane, WM_LBUTTONUP, 0, 0) ||
|
||||
!PostMessageW(pane, kWmCommitUnicodeScalar, 0, 0)) {
|
||||
g_unicode_input_active = false;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool queue_unicode_input(HWND pane, const std::uint32_t scalar) {
|
||||
if (pane == nullptr || scalar == 0 || scalar > 0x10ffff ||
|
||||
(scalar >= 0xd800 && scalar <= 0xdfff)) return false;
|
||||
if (g_pending_unicode_inputs.size() >= kMaxPendingUnicodeInput)
|
||||
return false;
|
||||
try {
|
||||
g_pending_unicode_inputs.push_back({-1, 0, scalar, 0});
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
return start_next_unicode_input(pane);
|
||||
}
|
||||
|
||||
bool insert_unicode_scalar(HWND pane, const std::uint32_t scalar) {
|
||||
return scalar >= 0x20 && queue_unicode_input(pane, scalar);
|
||||
}
|
||||
|
||||
void insert_unicode_text(HWND pane, const std::wstring& text) {
|
||||
for (std::size_t index = 0; index < text.size();) {
|
||||
std::uint32_t scalar = static_cast<std::uint16_t>(text[index++]);
|
||||
if (scalar >= 0xd800 && scalar <= 0xdbff && index < text.size()) {
|
||||
const std::uint32_t low = static_cast<std::uint16_t>(text[index]);
|
||||
if (low >= 0xdc00 && low <= 0xdfff) {
|
||||
++index;
|
||||
scalar = 0x10000 + ((scalar - 0xd800) << 10) +
|
||||
(low - 0xdc00);
|
||||
}
|
||||
}
|
||||
if (scalar == L'\r' || scalar == L'\n' || scalar == L'\t')
|
||||
queue_unicode_input(pane, scalar);
|
||||
else
|
||||
insert_unicode_scalar(pane, scalar);
|
||||
}
|
||||
}
|
||||
|
||||
LRESULT CALLBACK document_pane_proc(HWND pane, UINT message,
|
||||
WPARAM w_param, LPARAM l_param) {
|
||||
WNDPROC original = original_pane_proc(pane);
|
||||
if (message == kWmCommitUnicodeScalar) {
|
||||
if (g_unicode_input_active) {
|
||||
int doc_after = -1;
|
||||
long cp_after = 0;
|
||||
long cp_lim_after = 0;
|
||||
if (OpusGetUnicodeSelection(
|
||||
&doc_after, &cp_after, &cp_lim_after) &&
|
||||
doc_after == g_active_unicode_input.doc &&
|
||||
cp_after > g_active_unicode_input.cp) {
|
||||
if (g_active_unicode_input.scalar >= 0x20)
|
||||
OpusUnicodeSetScalar(
|
||||
g_active_unicode_input.doc,
|
||||
g_active_unicode_input.cp,
|
||||
g_active_unicode_input.scalar);
|
||||
g_unicode_input_active = false;
|
||||
InvalidateRect(pane, nullptr, FALSE);
|
||||
start_next_unicode_input(pane);
|
||||
} else if (doc_after == g_active_unicode_input.doc &&
|
||||
g_active_unicode_input.retries++ < 8) {
|
||||
PostMessageW(pane, WM_LBUTTONUP, 0, 0);
|
||||
PostMessageW(pane, kWmCommitUnicodeScalar, 0, 0);
|
||||
} else {
|
||||
g_unicode_input_active = false;
|
||||
start_next_unicode_input(pane);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (message == WM_UNICHAR) {
|
||||
if (w_param == UNICODE_NOCHAR) return TRUE;
|
||||
insert_unicode_scalar(pane, static_cast<std::uint32_t>(w_param));
|
||||
return 0;
|
||||
}
|
||||
/* The original message loop marks already-translated byte input by
|
||||
setting bit 15 (0x80xx). That is an internal Word flag, not a Unicode
|
||||
code point, and must reach the legacy pane procedure unchanged. */
|
||||
if (message == WM_CHAR && (w_param & 0xff00) != 0x8000 &&
|
||||
w_param >= 0x80) {
|
||||
OpusQueueUnicodeWmChar(
|
||||
pane, static_cast<unsigned int>(w_param));
|
||||
return 0;
|
||||
}
|
||||
if (message == WM_IME_COMPOSITION &&
|
||||
(l_param & GCS_RESULTSTR) != 0) {
|
||||
HIMC context = ImmGetContext(pane);
|
||||
if (context != nullptr) {
|
||||
const LONG byte_count = ImmGetCompositionStringW(
|
||||
context, GCS_RESULTSTR, nullptr, 0);
|
||||
if (byte_count > 0 && byte_count <= 1024 * 1024 &&
|
||||
(byte_count % sizeof(wchar_t)) == 0) {
|
||||
std::wstring result(
|
||||
static_cast<std::size_t>(byte_count) / sizeof(wchar_t),
|
||||
L'\0');
|
||||
if (ImmGetCompositionStringW(context, GCS_RESULTSTR,
|
||||
&result[0], static_cast<DWORD>(byte_count)) ==
|
||||
byte_count) {
|
||||
insert_unicode_text(pane, result);
|
||||
}
|
||||
}
|
||||
ImmReleaseContext(pane, context);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (message == WM_MOUSEWHEEL) {
|
||||
if (g_document_wheel.pane != pane) {
|
||||
g_document_wheel = {};
|
||||
@@ -2509,6 +2732,9 @@ LRESULT CALLBACK document_pane_proc(HWND pane, UINT message,
|
||||
if (g_document_wheel.pane == pane) {
|
||||
g_document_wheel = {};
|
||||
}
|
||||
g_pending_unicode_inputs.clear();
|
||||
g_unicode_input_active = false;
|
||||
g_pending_high_surrogate = 0;
|
||||
clear_page_snapshots(pane);
|
||||
RemovePropW(pane, kOriginalPaneProcProperty);
|
||||
if (original != nullptr) {
|
||||
@@ -2590,6 +2816,15 @@ LRESULT CALLBACK app_window_proc(HWND app, UINT message,
|
||||
OpusExportCurrentDocumentPdf();
|
||||
return 0;
|
||||
}
|
||||
if (command >= kCmdLanguageBase &&
|
||||
command < kCmdLanguageBase + kLanguageChoices.size()) {
|
||||
const LanguageChoice& choice =
|
||||
kLanguageChoices[command - kCmdLanguageBase];
|
||||
OpusUnicodeSetInputLanguage(choice.tag);
|
||||
update_language_menu_check();
|
||||
DrawMenuBar(app);
|
||||
return 0;
|
||||
}
|
||||
if (command == kCmdToggleStandardToolbar) {
|
||||
toggle_toolbar_row(app, toolbar, true);
|
||||
return 0;
|
||||
@@ -2599,6 +2834,7 @@ LRESULT CALLBACK app_window_proc(HWND app, UINT message,
|
||||
return 0;
|
||||
}
|
||||
} else if (message == WM_INITMENUPOPUP) {
|
||||
update_language_menu_check();
|
||||
HWND toolbar = FindWindowExW(app, nullptr, kToolbarClass, nullptr);
|
||||
ToolbarState* state = toolbar_state(toolbar);
|
||||
if (state != nullptr) {
|
||||
@@ -2866,6 +3102,27 @@ bool register_ruler_overlay_class() {
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" int OpusQueueUnicodeWmChar(
|
||||
HWND pane, const unsigned int code_unit) {
|
||||
if (code_unit >= 0xd800 && code_unit <= 0xdbff) {
|
||||
g_pending_high_surrogate = static_cast<wchar_t>(code_unit);
|
||||
return TRUE;
|
||||
}
|
||||
std::uint32_t scalar = code_unit;
|
||||
if (code_unit >= 0xdc00 && code_unit <= 0xdfff) {
|
||||
if (g_pending_high_surrogate < 0xd800 ||
|
||||
g_pending_high_surrogate > 0xdbff) {
|
||||
g_pending_high_surrogate = 0;
|
||||
return FALSE;
|
||||
}
|
||||
scalar = 0x10000 +
|
||||
((static_cast<std::uint32_t>(g_pending_high_surrogate) -
|
||||
0xd800) << 10) + (code_unit - 0xdc00);
|
||||
}
|
||||
g_pending_high_surrogate = 0;
|
||||
return insert_unicode_scalar(pane, scalar);
|
||||
}
|
||||
|
||||
extern "C" void OpusDrawWin95HorizontalRuler(HWND ruler) {
|
||||
HDC dc = GetDC(ruler);
|
||||
if (dc != nullptr) {
|
||||
|
||||
@@ -653,7 +653,7 @@ int wmain(const int argument_count, wchar_t** arguments) {
|
||||
std::cerr <<
|
||||
"usage: opus_word1_ui_test WORD1.exe "
|
||||
"[--typing|--interaction|--selection|--caret|--formatting|--color|"
|
||||
"--font-typing|--clipboard|--about|--save-as|--pdf-export|"
|
||||
"--font-typing|--unicode|--clipboard|--about|--save-as|--pdf-export|"
|
||||
"--docx-open FILE|--docx-format-open FILE|"
|
||||
"--docx-pdf-export FILE]\n";
|
||||
return 1;
|
||||
@@ -678,6 +678,9 @@ int wmain(const int argument_count, wchar_t** arguments) {
|
||||
const bool font_typing_mode =
|
||||
argument_count == 3 &&
|
||||
std::wcscmp(arguments[2], L"--font-typing") == 0;
|
||||
const bool unicode_mode =
|
||||
argument_count == 3 &&
|
||||
std::wcscmp(arguments[2], L"--unicode") == 0;
|
||||
const bool about_mode =
|
||||
argument_count == 3 &&
|
||||
std::wcscmp(arguments[2], L"--about") == 0;
|
||||
@@ -703,7 +706,7 @@ int wmain(const int argument_count, wchar_t** arguments) {
|
||||
formatted_docx_open_mode);
|
||||
if (argument_count == 3 && !typing_mode && !interaction_mode &&
|
||||
!selection_mode && !caret_mode && !formatting_mode && !color_mode &&
|
||||
!font_typing_mode && !clipboard_mode && !about_mode &&
|
||||
!font_typing_mode && !unicode_mode && !clipboard_mode && !about_mode &&
|
||||
!save_as_mode && !pdf_export_mode) {
|
||||
std::cerr << "unknown test mode\n";
|
||||
return 1;
|
||||
@@ -1097,6 +1100,67 @@ int wmain(const int argument_count, wchar_t** arguments) {
|
||||
CloseHandle(process.hProcess);
|
||||
return 0;
|
||||
}
|
||||
if (unicode_mode) {
|
||||
const HWND pane = find_descendant_by_class(main_window, L"OpusWwd");
|
||||
DWORD ignored_process_id = 0;
|
||||
const DWORD thread_id =
|
||||
GetWindowThreadProcessId(main_window, &ignored_process_id);
|
||||
if (pane == nullptr ||
|
||||
!make_foreground_and_focus(main_window, pane, thread_id)) {
|
||||
return fail(process, 87,
|
||||
"Unicode test could not focus the document pane");
|
||||
}
|
||||
const LRESULT cp_first = SendMessageW(
|
||||
pane, kWmOpusX64QuerySelection, 0, 0);
|
||||
const std::array<std::uint32_t, 5> scalars{
|
||||
0x041f, 0x03a9, 0x0645, 0x3053, 0x1f642};
|
||||
for (std::size_t index = 0; index < scalars.size(); ++index) {
|
||||
const std::uint32_t scalar = scalars[index];
|
||||
bool accepted = false;
|
||||
if (index == 0) {
|
||||
DWORD_PTR result = 0;
|
||||
accepted = SendMessageTimeoutW(
|
||||
pane, WM_UNICHAR, static_cast<WPARAM>(scalar), 1,
|
||||
SMTO_ABORTIFHUNG | SMTO_BLOCK, 3000, &result) != FALSE;
|
||||
} else {
|
||||
accepted = SendMessageW(
|
||||
pane, kWmOpusX64QuerySelection, 107,
|
||||
static_cast<LPARAM>(scalar)) != FALSE;
|
||||
}
|
||||
if (!accepted) {
|
||||
return fail(process, 88,
|
||||
"Unicode input message was not accepted");
|
||||
}
|
||||
}
|
||||
Sleep(500);
|
||||
const LRESULT cp_after = SendMessageW(
|
||||
pane, kWmOpusX64QuerySelection, 0, 0);
|
||||
bool scalars_preserved = cp_after ==
|
||||
cp_first + static_cast<LRESULT>(scalars.size());
|
||||
std::array<LRESULT, 5> actual_scalars{};
|
||||
for (std::size_t index = 0;
|
||||
index < scalars.size(); ++index) {
|
||||
actual_scalars[index] = SendMessageW(
|
||||
pane, kWmOpusX64QuerySelection, 106,
|
||||
cp_first + static_cast<LRESULT>(index));
|
||||
scalars_preserved = scalars_preserved &&
|
||||
actual_scalars[index] == scalars[index];
|
||||
}
|
||||
if (!scalars_preserved) {
|
||||
std::cerr << "Unicode input range=" << cp_first << "->"
|
||||
<< cp_after << " scalars=";
|
||||
for (const LRESULT actual : actual_scalars)
|
||||
std::cerr << actual << ',';
|
||||
std::cerr << '\n';
|
||||
return fail(process, 89,
|
||||
"Unicode code points were not preserved in the document");
|
||||
}
|
||||
TerminateProcess(process.hProcess, 0);
|
||||
WaitForSingleObject(process.hProcess, 2000);
|
||||
CloseHandle(process.hThread);
|
||||
CloseHandle(process.hProcess);
|
||||
return 0;
|
||||
}
|
||||
if (font_typing_mode) {
|
||||
const HWND pane = find_descendant_by_class(main_window, L"OpusWwd");
|
||||
DWORD ignored_process_id = 0;
|
||||
|
||||
Reference in New Issue
Block a user