Added unicode support.

This commit is contained in:
Justin Marshall
2026-08-11 01:45:43 -07:00
parent 6d95e7d78d
commit 93ac3c1685
18 changed files with 1614 additions and 94 deletions
+5 -1
View File
@@ -865,7 +865,7 @@ target_compile_features(WORD1 PRIVATE cxx_std_20)
target_compile_definitions(WORD1 PRIVATE target_compile_definitions(WORD1 PRIVATE
WIN WIN23 CRLF NONATIVE OPUS_X64 NOMINMAX UNICODE _UNICODE 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) if(MSVC)
target_compile_options(WORD1 PRIVATE /J /W3 /wd4005 /utf-8 /guard:cf /sdl) 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) 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 COMMAND $<TARGET_FILE:opus_word1_ui_test> $<TARGET_FILE:WORD1> --clipboard
) )
set_tests_properties(opus_word1_clipboard_shortcut_test PROPERTIES TIMEOUT 20) 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 add_test(NAME opus_word1_typing_test
COMMAND $<TARGET_FILE:opus_word1_ui_test> $<TARGET_FILE:WORD1> --typing COMMAND $<TARGET_FILE:opus_word1_ui_test> $<TARGET_FILE:WORD1> --typing
) )
+1
View File
@@ -479,6 +479,7 @@ ChangeClipboard()
if (vsab.fOwnClipboard) if (vsab.fOwnClipboard)
{ /* only set handles if we really have something in docScrap */ { /* only set handles if we really have something in docScrap */
SetClipboardData( CF_OWNERDISPLAY, NULL ); SetClipboardData( CF_OWNERDISPLAY, NULL );
SetClipboardData( CF_UNICODETEXT, NULL );
/* don't put out cf_tiff or other rejected pic types, or /* don't put out cf_tiff or other rejected pic types, or
rtf or link for any of those types rtf or link for any of those types
*/ */
+49 -3
View File
@@ -70,6 +70,11 @@ extern char szEmpty[];
extern struct SCI vsci; extern struct SCI vsci;
extern int vwWinVersion; extern int vwWinVersion;
extern int wwCur; extern int wwCur;
#ifdef OPUS_X64
extern HANDLE OpusUnicodeCreateClipboardHandle();
extern int OpusUnicodeClipboardToLegacy();
extern int OpusUnicodeBindPendingClipboard();
#endif
extern LPCH LpchIncr(); extern LPCH LpchIncr();
@@ -279,6 +284,21 @@ int *pfBlankPic; /* set true if an empty picture; else ignored */
AssureLegalSel (PcaSet(&ca, doc, cpFirst, cpLim)); AssureLegalSel (PcaSet(&ca, doc, cpFirst, cpLim));
switch (cf) 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: case CF_TEXT:
return HWriteText (ca.doc, ca.cpFirst, ca.cpLim, cbInitial); return HWriteText (ca.doc, ca.cpFirst, ca.cpLim, cbInitial);
@@ -845,7 +865,7 @@ int FReadExtScrap()
{ {
vsab.fMayHavePic = vsab.fPict = fTrue; vsab.fMayHavePic = vsab.fPict = fTrue;
} }
if (cf != CF_TEXT) if (cf != CF_TEXT && cf != CF_UNICODETEXT)
vsab.fFormatted = fTrue; vsab.fFormatted = fTrue;
break; break;
} }
@@ -884,6 +904,8 @@ int cbInitial;
{ {
int docDest; int docDest;
BOOL fOk; BOOL fOk;
BOOL fUnicode = fFalse;
HANDLE hConverted = NULL;
struct CA ca; struct CA ca;
#ifdef BZ #ifdef BZ
@@ -892,6 +914,18 @@ int cbInitial;
switch (cf) 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_TEXT:
case CF_DIB: case CF_DIB:
case CF_BITMAP: case CF_BITMAP:
@@ -917,7 +951,11 @@ int cbInitial;
docDest = docScrap; docDest = docScrap;
/* otherwise use vdocScratch */ /* otherwise use vdocScratch */
else if ((docDest = DocCreateScratch (pca->doc)) == docNil) else if ((docDest = DocCreateScratch (pca->doc)) == docNil)
{
if (hConverted != NULL)
GlobalFree(hConverted);
return fFalse; return fFalse;
}
/* empty the destination doc and set styles, fonts to standard */ /* empty the destination doc and set styles, fonts to standard */
SetWholeDoc (docDest, PcaSetNil(&ca)); SetWholeDoc (docDest, PcaSetNil(&ca));
@@ -939,7 +977,11 @@ int cbInitial;
break; break;
case CF_TEXT: case CF_TEXT:
fOk = FReadText (docDest, cf, hData, cbInitial); fOk = FReadText (docDest, cf, hData, cbInitial);
if (fOk && fUnicode)
fOk = OpusUnicodeBindPendingClipboard(docDest);
} }
if (hConverted != NULL)
GlobalFree(hConverted);
fOk &= (!vmerr.fMemFail && !vmerr.fDiskFail); fOk &= (!vmerr.fMemFail && !vmerr.fDiskFail);
@@ -1843,7 +1885,8 @@ int cf;
BOOL fPrPic; BOOL fPrPic;
{ {
/* Desired order (win3 word: Notice text before picture formats): /* Desired order (Unicode text precedes legacy formats):
CF_UNICODETEXT
cfRTF cfRTF
CF_TEXT CF_TEXT
cfPRPic cfPRPic
@@ -1876,7 +1919,9 @@ BOOL fPrPic;
goto LMetafile; /* 4th or 5th choice */ goto LMetafile; /* 4th or 5th choice */
case cfNil: case cfNil:
return cfRTF; /* 1st choice */ return CF_UNICODETEXT; /* 1st choice */
case CF_UNICODETEXT:
return cfRTF;
case CF_TEXT: case CF_TEXT:
if (fPrPic && cfPrPic != cfNil) if (fPrPic && cfPrPic != cfNil)
return cfPrPic; /* 4th choice (Excel specific) */ return cfPrPic; /* 4th choice (Excel specific) */
@@ -1915,6 +1960,7 @@ BOOL fRestrictRTF;
switch (cf) switch (cf)
{ {
case CF_UNICODETEXT:
case CF_TEXT: case CF_TEXT:
/* reject text for either a normal pic or an import field pic, /* reject text for either a normal pic or an import field pic,
since it has no text result and would show nothing */ since it has no text result and would show nothing */
+6
View File
@@ -813,6 +813,9 @@ int doc;
int isels; int isels;
struct DOD *pdod, dod; struct DOD *pdod, dod;
extern struct PL **vhplbmc; extern struct PL **vhplbmc;
#ifdef OPUS_X64
extern void OpusUnicodeForgetDocument();
#endif
Assert(doc >= 0 && doc < docMac); Assert(doc >= 0 && doc < docMac);
Assert(doc == docNil || mpdochdod[doc] != hNil); Assert(doc == docNil || mpdochdod[doc] != hNil);
@@ -848,6 +851,9 @@ int doc;
vdocTemp = docNil; vdocTemp = docNil;
else if (doc == vdocScratch) else if (doc == vdocScratch)
vdocScratch = docNil; vdocScratch = docNil;
#ifdef OPUS_X64
OpusUnicodeForgetDocument(doc);
#endif
DisposeDocPdod(doc, &dod ); DisposeDocPdod(doc, &dod );
Assert (vdocScratch == docNil || PdodDoc(vdocScratch)->doc Assert (vdocScratch == docNil || PdodDoc(vdocScratch)->doc
+15
View File
@@ -73,6 +73,8 @@ BOOL vfNoInval=0; /* can be > 1 */
#ifdef OPUS_X64 #ifdef OPUS_X64
struct PLC **HplcCreateEdc(); struct PLC **HplcCreateEdc();
extern void OpusUnicodeOnReplace();
extern void OpusUnicodeOnReplaceCps();
#else #else
struct PLC *HplcCreateEdc(); struct PLC *HplcCreateEdc();
#endif #endif
@@ -463,6 +465,9 @@ struct CA *pca;
int fn; int fn;
FC fc, dfc; FC fc, dfc;
{ {
int docUnicode = pca->doc;
CP cpFirstUnicode = pca->cpFirst;
CP cpLimUnicode = pca->cpLim;
struct XBC xbc; struct XBC xbc;
struct XSR *pxsr; struct XSR *pxsr;
/* this should be the maximum number of calls to XReplace or XReplaceCps that /* this should be the maximum number of calls to XReplace or XReplaceCps that
@@ -489,6 +494,7 @@ should go off) */
XReplace(fFalse, &xbc, pxsr); XReplace(fFalse, &xbc, pxsr);
EndCommit(); EndCommit();
CloseTns(&xbc); CloseTns(&xbc);
OpusUnicodeOnReplace(docUnicode, cpFirstUnicode, cpLimUnicode, dfc);
return fTrue; return fTrue;
} }
@@ -821,6 +827,12 @@ on failure (in which case document is unchanged) */
HANDNATIVE BOOL C_FReplaceCps(pcaDel, pcaIns) HANDNATIVE BOOL C_FReplaceCps(pcaDel, pcaIns)
struct CA *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 XBC xbc;
struct XSR *pxsr; struct XSR *pxsr;
/* this should be the maximum number of calls to XReplace or XReplaceCps that /* 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); XReplaceCps(fFalse, &xbc, pxsr);
EndCommit(); EndCommit();
CloseTns(&xbc); CloseTns(&xbc);
OpusUnicodeOnReplaceCps(docDelUnicode, cpDelFirstUnicode,
cpDelLimUnicode, docInsUnicode, cpInsFirstUnicode,
cpInsLimUnicode);
return fTrue; return fTrue;
} }
+1
View File
@@ -1808,6 +1808,7 @@ BOOL fPasteLink;
{ {
if ((fPasteLink && fmt == cfLink) if ((fPasteLink && fmt == cfLink)
|| (!fPasteLink && (fmt == cfRTF || || (!fPasteLink && (fmt == cfRTF ||
fmt == CF_UNICODETEXT ||
fmt == CF_TEXT || fmt == CF_TEXT ||
fmt == CF_BITMAP || fmt == CF_BITMAP ||
fmt == CF_METAFILEPICT))) fmt == CF_METAFILEPICT)))
+24 -7
View File
@@ -128,6 +128,10 @@ extern struct DBS vdbs;
extern struct PREF vpref; extern struct PREF vpref;
extern struct PRI vpri; extern struct PRI vpri;
#ifdef OPUS_X64
extern int OpusUnicodeExtTextOut();
extern int OpusUnicodeHasRange();
#endif
extern int vlm; extern int vlm;
extern int vfPrvwDisp; extern int vfPrvwDisp;
@@ -1256,13 +1260,26 @@ LDacNop:
else else
{ {
LRealExtTextOut: LRealExtTextOut:
ExtTextOut(hdc, xp, yp, #ifdef OPUS_X64
eto, /* action bits */ ExtTextOut(hdc, xp, yp, eto,
fRcAdjusted ? (LPRECT)&rcAdjusted : fRcAdjusted ? (LPRECT)&rcAdjusted :
(LPRECT)&rcOpaque, /* opaque rect */ (LPRECT)&rcOpaque,
(LPCH)&vfli.rgch [ich], /* string */ (LPCH)&vfli.rgch [ich], cch, lpdxp);
cch, /* length */ if (OpusUnicodeHasRange(vfli.doc,
lpdxp ); /* lpdx */ 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; vrf.fInExternalCall = fFalse;
(char *) pchr = (char *)*vhgrpchr + bchrCur; (char *) pchr = (char *)*vhgrpchr + bchrCur;
+9
View File
@@ -1265,6 +1265,15 @@ LHaveEvent:
return fFalse; 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 (FIsKeyMessage((LPMSG) &vmsgLast))
{ {
if (vmsgLast.wParam & 0x8000) /* indicates we did xlation */ if (vmsgLast.wParam & 0x8000) /* indicates we did xlation */
+7 -3
View File
@@ -91,6 +91,7 @@ extern int OpusModernPendingDocxParagraphCount();
extern int OpusModernGetPendingDocxRun(); extern int OpusModernGetPendingDocxRun();
extern int OpusModernGetPendingDocxParagraph(); extern int OpusModernGetPendingDocxParagraph();
extern int OpusModernGetPendingDocxPage(); extern int OpusModernGetPendingDocxPage();
extern int OpusModernBindPendingDocxUnicode();
extern void OpusModernClearPendingDocxFormatting(); extern void OpusModernClearPendingDocxFormatting();
extern int OpusX64FtcFromFontNameForDoc(); extern int OpusX64FtcFromFontNameForDoc();
#endif #endif
@@ -128,8 +129,9 @@ int doc;
struct CA ca; struct CA ca;
char grpprl[96]; char grpprl[96];
char szFont[LF_FACESIZE]; char szFont[LF_FACESIZE];
char szLanguage[32];
int bold, italic, underline, strike, smallCaps, allCaps, hidden; int bold, italic, underline, strike, smallCaps, allCaps, hidden;
int hps, ico; int hps, ico, charset;
int jc, dxaLeft, dxaRight, dxaLeft1, dyaBefore, dyaAfter, dyaLine; int jc, dxaLeft, dxaRight, dxaLeft1, dyaBefore, dyaAfter, dyaLine;
int fKeep, fKeepFollow, fPageBreakBefore, fBottomBorder; int fKeep, fKeepFollow, fPageBreakBefore, fBottomBorder;
int xaPage, yaPage, dxaLeftPage, dxaRightPage, dyaTopPage, dyaBottomPage; int xaPage, yaPage, dxaLeftPage, dxaRightPage, dyaTopPage, dyaBottomPage;
@@ -195,7 +197,8 @@ int doc;
{ {
if (!OpusModernGetPendingDocxRun(i, &cpFirst, &cpLim, &bold, if (!OpusModernGetPendingDocxRun(i, &cpFirst, &cpLim, &bold,
&italic, &underline, &strike, &smallCaps, &allCaps, &italic, &underline, &strike, &smallCaps, &allCaps,
&hidden, &hps, &ico, szFont, sizeof(szFont))) &hidden, &hps, &ico, szFont, sizeof(szFont), &charset,
szLanguage, sizeof(szLanguage)))
continue; continue;
if (cpFirst < cp0 || cpFirst >= cpMac) if (cpFirst < cp0 || cpFirst >= cpMac)
continue; continue;
@@ -214,7 +217,7 @@ int doc;
cb = CbAppendDocxPrl(grpprl, cb, sizeof(grpprl), sprmCFVanish, hidden); cb = CbAppendDocxPrl(grpprl, cb, sizeof(grpprl), sprmCFVanish, hidden);
cb = CbAppendDocxPrl(grpprl, cb, sizeof(grpprl), sprmCHps, hps); cb = CbAppendDocxPrl(grpprl, cb, sizeof(grpprl), sprmCHps, hps);
cb = CbAppendDocxPrl(grpprl, cb, sizeof(grpprl), sprmCIco, ico); cb = CbAppendDocxPrl(grpprl, cb, sizeof(grpprl), sprmCIco, ico);
ftc = OpusX64FtcFromFontNameForDoc(doc, szFont); ftc = OpusX64FtcFromFontNameForDoc(doc, szFont, charset);
if (ftc >= 0) if (ftc >= 0)
cb = CbAppendDocxPrl(grpprl, cb, sizeof(grpprl), sprmCFtc, ftc); cb = CbAppendDocxPrl(grpprl, cb, sizeof(grpprl), sprmCFtc, ftc);
ca.doc = doc; ca.doc = doc;
@@ -224,6 +227,7 @@ int doc;
} }
PdodDoc(doc)->fFormatted = fTrue; PdodDoc(doc)->fFormatted = fTrue;
OpusModernBindPendingDocxUnicode(doc);
OpusModernClearPendingDocxFormatting(); OpusModernClearPendingDocxFormatting();
} }
#endif #endif
+69 -12
View File
@@ -105,6 +105,24 @@ static int vWin95ZoomPercent = 100;
static int vWin95BaseDxsInch = 0; static int vWin95BaseDxsInch = 0;
static int vWin95BaseDysInch = 0; static int vWin95BaseDysInch = 0;
int vOpusPdfExportStage = 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() int OpusExportCurrentDocumentPdf()
{ {
@@ -117,14 +135,19 @@ int OpusExportCurrentDocumentPdf()
extern CHAR HUGE *vhpchFetch; extern CHAR HUGE *vhpchFetch;
extern int OpusPdfSnapshotBegin(); extern int OpusPdfSnapshotBegin();
extern int OpusPdfSnapshotAddParagraph(); extern int OpusPdfSnapshotAddParagraph();
extern int OpusPdfSnapshotAddRun(); extern int OpusPdfSnapshotAddRunUtf8();
extern int OpusPdfSnapshotExportDialog(); extern int OpusPdfSnapshotExportDialog();
extern int OpusDocxSnapshotExportPath();
extern void OpusX64FontNameFromFtc(); extern void OpusX64FontNameFromFtc();
extern int OpusX64CharsetFromFtc();
extern int OpusUnicodeTextToUtf8();
extern int OpusUnicodeLanguageTagAt();
vOpusPdfExportStage = 1; vOpusPdfExportStage = 1;
if (selCur.doc == docNil) if (vOpusDocxSnapshotDoc == docNil && selCur.doc == docNil)
return fFalse; return fFalse;
doc = DocMother(selCur.doc); doc = vOpusDocxSnapshotDoc != docNil ? vOpusDocxSnapshotDoc :
DocMother(selCur.doc);
cpMac = CpMacDocEdit(doc); cpMac = CpMacDocEdit(doc);
if (cpMac < cp0 || cpMac > 0x3fffffffL) if (cpMac < cp0 || cpMac > 0x3fffffffL)
return fFalse; return fFalse;
@@ -159,43 +182,59 @@ int OpusExportCurrentDocumentPdf()
int ich; int ich;
int cch; int cch;
int cchOutput; int cchOutput;
char rgch[1024]; char rgch[4096];
char szFont[LF_FACESIZE]; char szFont[LF_FACESIZE];
char szLanguage[32];
int charset;
FetchCp(doc, cpRun, fcmChars + fcmProps); FetchCp(doc, cpRun, fcmChars + fcmProps);
if (vccpFetch <= 0 || vhpchFetch == NULL) if (vccpFetch <= 0 || vhpchFetch == NULL)
return fFalse; return fFalse;
cch = (int)CpMin((CP)vccpFetch, cpParaLim - cpRun); 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; cchOutput = 0;
for (ich = 0; ich < cch; ++ich) for (ich = 0; ich < cch; ++ich)
{ {
int ch = (unsigned char)vhpchFetch[ich]; int ch = (unsigned char)vhpchFetch[ich];
char chOutput;
char rgchUtf8[8];
int cchUtf8;
if (ch == chReturn || ch == chEop) if (ch == chReturn || ch == chEop)
continue; continue;
if (ch == chTable || ch == chTab) if (ch == chTable || ch == chTab)
rgch[cchOutput++] = '\t'; chOutput = '\t';
else if (ch == chSect || ch == chColumnBreak) else if (ch == chSect || ch == chColumnBreak)
rgch[cchOutput++] = '\f'; chOutput = '\f';
else if (ch == chNonReqHyphen) else if (ch == chNonReqHyphen)
rgch[cchOutput++] = '-'; chOutput = '-';
/* fSpec can cover imported field/list runs whose bytes are still /* fSpec can cover imported field/list runs whose bytes are still
ordinary visible text. Filter actual control codes above, not ordinary visible text. Filter actual control codes above, not
the whole run, or leading words disappear from PDF output. */ the whole run, or leading words disappear from PDF output. */
else if (ch >= chSpace) 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) if (cchOutput > 0)
{ {
rgch[cchOutput] = '\0'; rgch[cchOutput] = '\0';
OpusX64FontNameFromFtc(vchpFetch.ftc, OpusX64FontNameFromFtc(vchpFetch.ftc,
szFont, sizeof(szFont)); szFont, sizeof(szFont));
OpusUnicodeLanguageTagAt(doc, cpRun,
szLanguage, sizeof(szLanguage));
vOpusPdfExportStage = 100000 + (int)cpRun; vOpusPdfExportStage = 100000 + (int)cpRun;
if (!OpusPdfSnapshotAddRun(rgch, cchOutput, szFont, if (!OpusPdfSnapshotAddRunUtf8(rgch, cchOutput, szFont,
vchpFetch.hps, vchpFetch.fBold, vchpFetch.hps, vchpFetch.fBold,
vchpFetch.fItalic, vchpFetch.kul != kulNone, vchpFetch.fItalic, vchpFetch.kul != kulNone,
vchpFetch.fStrike, vchpFetch.fSmallCaps, vchpFetch.fStrike, vchpFetch.fSmallCaps,
vchpFetch.fCaps, vchpFetch.fVanish, vchpFetch.fCaps, vchpFetch.fVanish,
vchpFetch.ico)) vchpFetch.ico, szLanguage, charset))
return fFalse; return fFalse;
} }
cpRun += cch; cpRun += cch;
@@ -203,11 +242,29 @@ int OpusExportCurrentDocumentPdf()
cpPara = cpParaLim; cpPara = cpParaLim;
} }
vOpusPdfExportStage = 3; vOpusPdfExportStage = 3;
result = OpusPdfSnapshotExportDialog(vhwndApp); result = vszOpusDocxSnapshotPath != NULL ?
OpusDocxSnapshotExportPath(vszOpusDocxSnapshotPath) :
OpusPdfSnapshotExportDialog(vhwndApp);
vOpusPdfExportStage = result ? 4 : -4; vOpusPdfExportStage = result ? 4 : -4;
return result; 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 /* The Word 1.x Color command opens a keyboard prompt. The Win95 chrome
supplies a palette instead, but still applies the original character supplies a palette instead, but still applies the original character
property so document storage and formatting behavior remain unchanged. */ property so document storage and formatting behavior remain unchanged. */
+1 -1
View File
@@ -708,7 +708,7 @@ LCleanup:
FWin95SaveAliasMatches(stFile) && PdodDoc(doc)->fn != fnNil) FWin95SaveAliasMatches(stFile) && PdodDoc(doc)->fn != fnNil)
FCloseFn(PdodDoc(doc)->fn); FCloseFn(PdodDoc(doc)->fn);
if (!OpusFinishWin95SaveAlias(stFile, if (!OpusFinishWin95SaveAlias(stFile,
cmd == cmdOK && pcmb->fAction) && cmd == cmdOK) cmd == cmdOK && pcmb->fAction, doc) && cmd == cmdOK)
cmd = cmdError; cmd = cmdError;
#endif #endif
if (cmd == cmdCancelled) if (cmd == cmdCancelled)
+53 -14
View File
@@ -1002,27 +1002,50 @@ const char *sz;
/* Variant used while a DOCX is being opened. The new document has not yet /* 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 become selCur.doc, so the normal ribbon adapter would add the font to the
document that was active before File Open. */ document that was active before File Open. */
EXPORT int OpusX64FtcFromFontNameForDoc(doc, sz) EXPORT int OpusX64FtcFromFontNameForDoc(doc, sz, charset)
int doc; int doc;
const char *sz; const char *sz;
int charset;
{ {
int cch;
struct FFN *pffn;
char rgch[cbFfnLast] = {0};
if (sz == NULL || *sz == '\0') if (sz == NULL || *sz == '\0')
return wNinch; return wNinch;
/* Word 1.x keeps only three guaranteed GDI font slots in a new plain-text /* A new Word 1.x document exposes only the three guaranteed screen-font
document: Times, Symbol, and Helvetica. Adding Office-era fonts before slots while DOCX formatting is applied. Adding a normal ANSI font at
the imported document becomes current can alias the new ftc to Symbol. this point can alias it to slot 1 (Symbol), corrupting every Latin
Preserve the requested family safely; Helvetica is the closest available character in the document. Keep the historical safe mappings for ANSI
substitute for Aptos/Calibri/Arial and retains ANSI character mapping. */ faces; charset-specific runs still get real FFN records below. */
if (!FNeNcSz(sz, "Symbol") || !FNeNcSz(sz, "Wingdings")) if (!FNeNcSz(sz, "Symbol") || !FNeNcSz(sz, "Wingdings"))
return 1; return 1;
if (!FNeNcSz(sz, "Times New Roman") || !FNeNcSz(sz, "Times") || if (charset == ANSI_CHARSET || charset == DEFAULT_CHARSET)
!FNeNcSz(sz, "Georgia") || !FNeNcSz(sz, "Garamond") || {
!FNeNcSz(sz, "Cambria")) if (!FNeNcSz(sz, "Times New Roman") || !FNeNcSz(sz, "Times") ||
return ftcDefault; !FNeNcSz(sz, "Georgia") || !FNeNcSz(sz, "Garamond") ||
if (!FNeNcSz(sz, "Courier") || !FNeNcSz(sz, "Courier New") || !FNeNcSz(sz, "Cambria"))
!FNeNcSz(sz, "Consolas")) return ftcDefault;
return FtcFromDocIbst(doc, ibstCourier); if (!FNeNcSz(sz, "Courier") || !FNeNcSz(sz, "Courier New") ||
return 2; !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) EXPORT int OpusX64HpsFromFontSize(sz)
@@ -1067,6 +1090,22 @@ int cchMax;
bltbyte(szFont, sz, min(CchSz(szFont), cchMax)); bltbyte(szFont, sz, min(CchSz(szFont), cchMax));
sz[cchMax - 1] = '\0'; 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 #endif
+19
View File
@@ -96,6 +96,8 @@ extern struct FTI vfti;
extern char (**vhgrpchr)[]; extern char (**vhgrpchr)[];
extern int vbchrMac; extern int vbchrMac;
extern struct STTB **vhsttbFont; extern struct STTB **vhsttbFont;
extern unsigned int OpusUnicodeScalarAt();
extern int OpusQueueUnicodeWmChar();
#endif #endif
extern struct FCB **mpfnhfcb[]; extern struct FCB **mpfnhfcb[];
extern int vfnPreload; extern int vfnPreload;
@@ -1834,6 +1836,12 @@ LONG lParam;
extern int vOpusPdfExportStage; extern int vOpusPdfExportStage;
return (LRESULT)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; return (LRESULT) -1;
#endif #endif
@@ -2501,6 +2509,17 @@ LPMSG lpmsg;
wm = lpmsg->message; wm = lpmsg->message;
kc = lpmsg->wParam; /* key code or char */ 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 #ifdef COMING_SOON_TO_A_WORD_PROCESSOR_NEAR_YOU
File diff suppressed because it is too large Load Diff
+36 -2
View File
@@ -11,6 +11,8 @@ extern "C" int OpusModernDocxToRtfFile(const char*, const char*);
extern "C" int OpusModernDocxToTextFile(const char*, const char*); extern "C" int OpusModernDocxToTextFile(const char*, const char*);
extern "C" int OpusModernRtfFileToDocx(const char*, const char*); extern "C" int OpusModernRtfFileToDocx(const char*, const char*);
extern "C" int OpusModernRtfFileToPdf(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 OpusPdfSnapshotBegin(int, int, int, int, int, int);
extern "C" int OpusPdfSnapshotAddParagraph(int, int, int, int, int, int, extern "C" int OpusPdfSnapshotAddParagraph(int, int, int, int, int, 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 rtf = base + ".rtf";
const std::string docx = base + ".docx"; const std::string docx = base + ".docx";
const std::string roundtrip = base + ".roundtrip.rtf"; 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 oversized = base + ".oversized.rtf";
const std::string preserved = base + ".preserved.pdf"; const std::string preserved = base + ".preserved.pdf";
char requested_pdf[32768]{}; char requested_pdf[32768]{};
@@ -67,11 +70,33 @@ int main(const int argument_count, char** arguments) {
output << "{\\rtf1\\ansi{\\fonttbl{\\f0 Arial;}}" output << "{\\rtf1\\ansi{\\fonttbl{\\f0 Arial;}}"
"{\\colortbl;\\red255\\green0\\blue0;}" "{\\colortbl;\\red255\\green0\\blue0;}"
"\\f0\\fs24 Plain {\\b Bold} {\\i Italic} " "\\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 written = OpusModernRtfFileToDocx(rtf.c_str(), docx.c_str()) != 0;
const bool read = written && const bool read = written &&
OpusModernDocxToRtfFile(docx.c_str(), roundtrip.c_str()) != 0; 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; const bool pdf_written = OpusModernRtfFileToPdf(rtf.c_str(), pdf.c_str()) != 0;
{ {
std::ofstream output(preserved, std::ios::binary); std::ofstream output(preserved, std::ios::binary);
@@ -115,17 +140,26 @@ int main(const int argument_count, char** arguments) {
DeleteFileA(rtf.c_str()); DeleteFileA(rtf.c_str());
DeleteFileA(docx.c_str()); DeleteFileA(docx.c_str());
DeleteFileA(roundtrip.c_str()); DeleteFileA(roundtrip.c_str());
DeleteFileA(imported_text.c_str());
DeleteFileA(oversized.c_str()); DeleteFileA(oversized.c_str());
DeleteFileA(preserved.c_str()); DeleteFileA(preserved.c_str());
if (!keep_pdf) DeleteFileA(pdf.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("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("\\b") == std::string::npos ||
result.find("\\cf1") == std::string::npos || !pdf_written || result.find("\\cf1") == std::string::npos || !pdf_written ||
!pdf_data.starts_with("%PDF-") || !pdf_data.starts_with("%PDF-") ||
pdf_data.find("Plain") == std::string::npos || pdf_data.find("Plain") == std::string::npos ||
pdf_data.find("Bold") == std::string::npos || pdf_data.find("Bold") == std::string::npos ||
pdf_data.find("Second paragraph") == 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("/Helvetica-Bold") == std::string::npos ||
pdf_data.find("xref") == std::string::npos || pdf_data.find("xref") == std::string::npos ||
pdf_data.find("%%EOF") == std::string::npos || pdf_data.find("%%EOF") == std::string::npos ||
+5 -5
View File
@@ -26,6 +26,7 @@ int FSetCabSz(void**, const char*, std::uint16_t);
int OpusModernPathIsDocx(const char*); int OpusModernPathIsDocx(const char*);
int OpusModernDocxToTextFile(const char*, const char*); int OpusModernDocxToTextFile(const char*, const char*);
int OpusModernRtfFileToDocx(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, 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, int OpusFinishWin95SaveAlias(const unsigned char* st_file,
const int success) { const int success, const int doc) {
const std::string path = counted_path(st_file); const std::string path = counted_path(st_file);
if (path.empty()) { if (path.empty()) {
return !g_win95_save_alias.active; return !g_win95_save_alias.active;
@@ -2541,9 +2542,8 @@ int OpusFinishWin95SaveAlias(const unsigned char* st_file,
if (copied) { if (copied) {
copied = OpusModernPathIsDocx( copied = OpusModernPathIsDocx(
g_win95_save_alias.selected_path.c_str()) ? g_win95_save_alias.selected_path.c_str()) ?
OpusModernRtfFileToDocx( OpusSaveDocumentAsDocx(
g_win95_save_alias.legacy_path.c_str(), doc, g_win95_save_alias.selected_path.c_str()) != 0 :
g_win95_save_alias.selected_path.c_str()) != 0 :
atomic_copy_file(g_win95_save_alias.legacy_path, atomic_copy_file(g_win95_save_alias.legacy_path,
g_win95_save_alias.selected_path); g_win95_save_alias.selected_path);
} }
@@ -2565,7 +2565,7 @@ int OpusFinishWin95SaveAlias(const unsigned char* st_file,
return false; return false;
} }
return OpusModernPathIsDocx(saved->second.c_str()) ? 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); atomic_copy_file(path, saved->second);
} }
+257
View File
@@ -1,10 +1,12 @@
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include <windows.h> #include <windows.h>
#include <windowsx.h> #include <windowsx.h>
#include <imm.h>
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <cstdint> #include <cstdint>
#include <deque>
#include <string> #include <string>
#include <vector> #include <vector>
@@ -36,6 +38,14 @@ extern "C" int OpusAdjustWin95HorizontalMargin(
HWND ruler, int left_margin, int delta_pixels); HWND ruler, int left_margin, int delta_pixels);
extern "C" void OpusDrawWin95HorizontalRuler(HWND ruler); extern "C" void OpusDrawWin95HorizontalRuler(HWND ruler);
extern "C" int OpusExportCurrentDocumentPdf(void); 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 { namespace {
@@ -57,7 +67,10 @@ constexpr UINT kCmdToggleStandardToolbar = 0x7101;
constexpr UINT kCmdToggleFormattingToolbar = 0x7102; constexpr UINT kCmdToggleFormattingToolbar = 0x7102;
constexpr UINT kCmdExportPdf = 0x7103; constexpr UINT kCmdExportPdf = 0x7103;
constexpr UINT kCmdTextColorBase = 0x7200; constexpr UINT kCmdTextColorBase = 0x7200;
constexpr UINT kCmdLanguageBase = 0x7300;
constexpr UINT_PTR kSyncTimer = 0x951; constexpr UINT_PTR kSyncTimer = 0x951;
constexpr UINT kWmCommitUnicodeScalar = WM_APP + 0x452;
constexpr std::size_t kMaxPendingUnicodeInput = 0xffff;
constexpr wchar_t kOriginalPaneProcProperty[] = L"OpusWord95OriginalPaneProc"; constexpr wchar_t kOriginalPaneProcProperty[] = L"OpusWord95OriginalPaneProc";
enum class FormatGlyph { enum class FormatGlyph {
@@ -168,8 +181,47 @@ struct ToolbarState {
HBRUSH g_menu_brush = nullptr; HBRUSH g_menu_brush = nullptr;
HMENU g_table_menu = nullptr; HMENU g_table_menu = nullptr;
HMENU g_toolbars_menu = nullptr; HMENU g_toolbars_menu = nullptr;
HMENU g_language_menu = nullptr;
WNDPROC g_original_app_proc = nullptr; WNDPROC g_original_app_proc = nullptr;
bool g_word95_page_view_active = false; 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 { struct VerticalRulerDragState {
HWND pane = nullptr; HWND pane = nullptr;
@@ -347,6 +399,33 @@ void configure_word95_menus(HWND window) {
MF_BYPOSITION | MF_POPUP | MF_STRING, MF_BYPOSITION | MF_POPUP | MF_STRING,
reinterpret_cast<UINT_PTR>(tools), L"&Tools"); 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)) { if (g_table_menu == nullptr || !IsMenu(g_table_menu)) {
g_table_menu = CreatePopupMenu(); 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, LRESULT CALLBACK document_pane_proc(HWND pane, UINT message,
WPARAM w_param, LPARAM l_param) { WPARAM w_param, LPARAM l_param) {
WNDPROC original = original_pane_proc(pane); 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 (message == WM_MOUSEWHEEL) {
if (g_document_wheel.pane != pane) { if (g_document_wheel.pane != pane) {
g_document_wheel = {}; g_document_wheel = {};
@@ -2509,6 +2732,9 @@ LRESULT CALLBACK document_pane_proc(HWND pane, UINT message,
if (g_document_wheel.pane == pane) { if (g_document_wheel.pane == pane) {
g_document_wheel = {}; g_document_wheel = {};
} }
g_pending_unicode_inputs.clear();
g_unicode_input_active = false;
g_pending_high_surrogate = 0;
clear_page_snapshots(pane); clear_page_snapshots(pane);
RemovePropW(pane, kOriginalPaneProcProperty); RemovePropW(pane, kOriginalPaneProcProperty);
if (original != nullptr) { if (original != nullptr) {
@@ -2590,6 +2816,15 @@ LRESULT CALLBACK app_window_proc(HWND app, UINT message,
OpusExportCurrentDocumentPdf(); OpusExportCurrentDocumentPdf();
return 0; 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) { if (command == kCmdToggleStandardToolbar) {
toggle_toolbar_row(app, toolbar, true); toggle_toolbar_row(app, toolbar, true);
return 0; return 0;
@@ -2599,6 +2834,7 @@ LRESULT CALLBACK app_window_proc(HWND app, UINT message,
return 0; return 0;
} }
} else if (message == WM_INITMENUPOPUP) { } else if (message == WM_INITMENUPOPUP) {
update_language_menu_check();
HWND toolbar = FindWindowExW(app, nullptr, kToolbarClass, nullptr); HWND toolbar = FindWindowExW(app, nullptr, kToolbarClass, nullptr);
ToolbarState* state = toolbar_state(toolbar); ToolbarState* state = toolbar_state(toolbar);
if (state != nullptr) { if (state != nullptr) {
@@ -2866,6 +3102,27 @@ bool register_ruler_overlay_class() {
} // namespace } // 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) { extern "C" void OpusDrawWin95HorizontalRuler(HWND ruler) {
HDC dc = GetDC(ruler); HDC dc = GetDC(ruler);
if (dc != nullptr) { if (dc != nullptr) {
+66 -2
View File
@@ -653,7 +653,7 @@ int wmain(const int argument_count, wchar_t** arguments) {
std::cerr << std::cerr <<
"usage: opus_word1_ui_test WORD1.exe " "usage: opus_word1_ui_test WORD1.exe "
"[--typing|--interaction|--selection|--caret|--formatting|--color|" "[--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-open FILE|--docx-format-open FILE|"
"--docx-pdf-export FILE]\n"; "--docx-pdf-export FILE]\n";
return 1; return 1;
@@ -678,6 +678,9 @@ int wmain(const int argument_count, wchar_t** arguments) {
const bool font_typing_mode = const bool font_typing_mode =
argument_count == 3 && argument_count == 3 &&
std::wcscmp(arguments[2], L"--font-typing") == 0; 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 = const bool about_mode =
argument_count == 3 && argument_count == 3 &&
std::wcscmp(arguments[2], L"--about") == 0; std::wcscmp(arguments[2], L"--about") == 0;
@@ -703,7 +706,7 @@ int wmain(const int argument_count, wchar_t** arguments) {
formatted_docx_open_mode); formatted_docx_open_mode);
if (argument_count == 3 && !typing_mode && !interaction_mode && if (argument_count == 3 && !typing_mode && !interaction_mode &&
!selection_mode && !caret_mode && !formatting_mode && !color_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) { !save_as_mode && !pdf_export_mode) {
std::cerr << "unknown test mode\n"; std::cerr << "unknown test mode\n";
return 1; return 1;
@@ -1097,6 +1100,67 @@ int wmain(const int argument_count, wchar_t** arguments) {
CloseHandle(process.hProcess); CloseHandle(process.hProcess);
return 0; 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) { if (font_typing_mode) {
const HWND pane = find_descendant_by_class(main_window, L"OpusWwd"); const HWND pane = find_descendant_by_class(main_window, L"OpusWwd");
DWORD ignored_process_id = 0; DWORD ignored_process_id = 0;