mirror of
https://github.com/jmarshall23/msword.git
synced 2026-08-11 23:50:53 +02:00
Compare commits
2 Commits
2b1165d869
...
93ac3c1685
| Author | SHA1 | Date | |
|---|---|---|---|
| 93ac3c1685 | |||
| 6d95e7d78d |
+10
-5
@@ -689,7 +689,7 @@ target_include_directories(opus_original_engine PRIVATE ${OPUS_ORIGINAL_INCLUDE_
|
||||
if(MSVC)
|
||||
# /J preserves the original compiler's unsigned-char default. Do not use
|
||||
# the C++ compiler for K&R-era original C translation units.
|
||||
target_compile_options(opus_original_engine PRIVATE /J /W3 /wd4005 /Gy)
|
||||
target_compile_options(opus_original_engine PRIVATE /J /W3 /wd4005 /Gy /guard:cf)
|
||||
endif()
|
||||
set_target_properties(opus_original_engine PROPERTIES
|
||||
FOLDER "Original Opus x64"
|
||||
@@ -731,13 +731,14 @@ target_compile_definitions(opus_x64_runtime PRIVATE
|
||||
NONATIVE
|
||||
OPUS_X64
|
||||
NOMINMAX
|
||||
$<$<CONFIG:Debug>:OPUS_TEST_HOOKS>
|
||||
)
|
||||
target_include_directories(opus_x64_runtime PRIVATE ${OPUS_ORIGINAL_INCLUDE_DIRS})
|
||||
target_link_libraries(opus_x64_runtime PUBLIC ole32 comdlg32 gdi32)
|
||||
if(MSVC)
|
||||
# Opus was built with unsigned plain characters; the byte-oriented string
|
||||
# and international-character assembly routines depend on that behavior.
|
||||
target_compile_options(opus_x64_runtime PRIVATE /J /W4 /permissive-)
|
||||
target_compile_options(opus_x64_runtime PRIVATE /J /W4 /permissive- /guard:cf /sdl)
|
||||
endif()
|
||||
set_target_properties(opus_x64_runtime PROPERTIES
|
||||
FOLDER "Original Opus x64/runtime"
|
||||
@@ -864,10 +865,10 @@ 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)
|
||||
target_link_options(WORD1 PRIVATE /DYNAMICBASE /HIGHENTROPYVA /NXCOMPAT /MANIFEST:NO)
|
||||
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)
|
||||
endif()
|
||||
set_target_properties(WORD1 PROPERTIES
|
||||
FOLDER "Original Opus x64/product"
|
||||
@@ -1005,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
|
||||
|
||||
@@ -81,6 +81,10 @@ int wLow, wHigh;
|
||||
int das;
|
||||
BOOL f;
|
||||
|
||||
#ifdef OPUS_X64
|
||||
/* DDE execute is unauthenticated cross-process macro execution. */
|
||||
goto LExeNACK;
|
||||
#endif
|
||||
if (fTerminating || PdcldDcl(dcl)->fExecuting || fElActive)
|
||||
goto LExeNACK;
|
||||
|
||||
@@ -117,6 +121,10 @@ LExeNACK:
|
||||
int das = dasNACK;
|
||||
int ibkmk;
|
||||
|
||||
#ifdef OPUS_X64
|
||||
/* Do not let another process overwrite an open document through DDE. */
|
||||
goto LNackPoke;
|
||||
#endif
|
||||
if (fTerminating)
|
||||
/* not much point in receiving while terminating */
|
||||
{
|
||||
|
||||
@@ -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)))
|
||||
@@ -2479,6 +2480,11 @@ uns atm;
|
||||
|
||||
Assert(atm < atmMax);
|
||||
|
||||
#ifdef OPUS_X64
|
||||
/* Never execute document/template macros merely because a file opened,
|
||||
closed, or Word started. See the secure default in elmisc.c. */
|
||||
return cmdOK;
|
||||
#endif
|
||||
|
||||
/* Skip auto macro if shift key is down */
|
||||
if (vfDisableAutoMacros || (GetAsyncKeyState(VK_SHIFT) & 0x8000))
|
||||
|
||||
+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;
|
||||
|
||||
@@ -80,7 +80,18 @@ extern BOOL vfAwfulNoise;
|
||||
extern int vfSeeSel;
|
||||
|
||||
|
||||
#ifdef OPUS_X64
|
||||
/*
|
||||
* Legacy Opus documents and templates can contain AutoOpen/AutoExec macros.
|
||||
* The original application ran them without a trust boundary. That is not
|
||||
* an acceptable default for documents received from modern file systems, so
|
||||
* the x64 port keeps automatic macros disabled. Explicitly invoked macros
|
||||
* continue to use the normal command path.
|
||||
*/
|
||||
BOOL vfDisableAutoMacros = fTrue;
|
||||
#else
|
||||
BOOL vfDisableAutoMacros = fFalse;
|
||||
#endif
|
||||
|
||||
/* Simple Cursor Movement / Selection Commands */
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -620,6 +620,7 @@ BOOL fTutorial;
|
||||
if (!fTutorial)
|
||||
{
|
||||
/* BLOCK: now run macros specified on command line */
|
||||
#ifndef OPUS_X64
|
||||
{
|
||||
int isz;
|
||||
CHAR * szArg;
|
||||
@@ -639,6 +640,7 @@ BOOL fTutorial;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* !OPUS_X64 */
|
||||
}
|
||||
|
||||
|
||||
|
||||
+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
|
||||
|
||||
+76
-12
@@ -104,6 +104,25 @@ extern struct REB * vpreb;
|
||||
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()
|
||||
{
|
||||
@@ -116,17 +135,24 @@ 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();
|
||||
|
||||
if (selCur.doc == docNil)
|
||||
vOpusPdfExportStage = 1;
|
||||
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;
|
||||
pdod = PdodMother(doc);
|
||||
vOpusPdfExportStage = 2;
|
||||
if (!OpusPdfSnapshotBegin(pdod->dop.xaPage, pdod->dop.yaPage,
|
||||
pdod->dop.dxaLeft, pdod->dop.dxaRight,
|
||||
pdod->dop.dyaTop < 0 ? -pdod->dop.dyaTop : pdod->dop.dyaTop,
|
||||
@@ -141,6 +167,7 @@ int OpusExportCurrentDocumentPdf()
|
||||
cpParaLim = CpMin(caPara.cpLim, cpMac);
|
||||
if (cpParaLim <= cpPara)
|
||||
cpParaLim = cpPara + 1;
|
||||
vOpusPdfExportStage = 1000 + (int)cpPara;
|
||||
if (!OpusPdfSnapshotAddParagraph(vpapFetch.jc,
|
||||
vpapFetch.dxaLeft, vpapFetch.dxaRight,
|
||||
vpapFetch.dxaLeft1, vpapFetch.dyaBefore,
|
||||
@@ -155,49 +182,86 @@ 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));
|
||||
if (!OpusPdfSnapshotAddRun(rgch, cchOutput, szFont,
|
||||
OpusUnicodeLanguageTagAt(doc, cpRun,
|
||||
szLanguage, sizeof(szLanguage));
|
||||
vOpusPdfExportStage = 100000 + (int)cpRun;
|
||||
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;
|
||||
}
|
||||
cpPara = cpParaLim;
|
||||
}
|
||||
result = OpusPdfSnapshotExportDialog(vhwndApp);
|
||||
vOpusPdfExportStage = 3;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
+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;
|
||||
@@ -1829,6 +1831,17 @@ LONG lParam;
|
||||
pffn = (struct FFN *)PstFromSttb(vhsttbFont, ibst);
|
||||
return (LRESULT)(unsigned char)ChsPffn(pffn);
|
||||
}
|
||||
case 105:
|
||||
{
|
||||
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
|
||||
@@ -2496,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
|
||||
|
||||
|
||||
@@ -143,14 +143,21 @@ int CchCurSzPathNat(char* path, const int drive) {
|
||||
if (path == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
/* The original ABI requires callers to provide exactly 67 bytes. Do not
|
||||
copy a modern MAX_PATH-sized current directory into that legacy buffer. */
|
||||
constexpr int kLegacyPathCapacity = 67;
|
||||
char current[MAX_PATH]{};
|
||||
if (_getdcwd(drive, current, static_cast<int>(sizeof(current))) ==
|
||||
nullptr) {
|
||||
path[0] = '\0';
|
||||
return 0;
|
||||
}
|
||||
std::strcpy(path, current);
|
||||
std::size_t length = std::strlen(path);
|
||||
std::size_t length = std::strlen(current);
|
||||
if (length + 2 > kLegacyPathCapacity) {
|
||||
path[0] = '\0';
|
||||
return 0;
|
||||
}
|
||||
std::memcpy(path, current, length + 1);
|
||||
if (length == 2 && path[1] == ':') {
|
||||
path[length++] = '\\';
|
||||
path[length] = '\0';
|
||||
|
||||
@@ -137,6 +137,12 @@ constexpr UINT kPromptMessages[] = {
|
||||
extern "C" {
|
||||
|
||||
LRESULT CALLBACK NatAppWndProc(HWND h, UINT m, WPARAM w, LPARAM l) {
|
||||
if (m == kWmDdeInitiate) {
|
||||
// The legacy DDE server exposes document contents and macro commands
|
||||
// to any process in the desktop session. The modern port does not
|
||||
// accept inbound DDE conversations.
|
||||
return 0;
|
||||
}
|
||||
return Dispatch(h, m, w, l, AppWndProc, kAppMessages);
|
||||
}
|
||||
LRESULT CALLBACK NatMwdWndProc(HWND h, UINT m, WPARAM w, LPARAM l) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,15 +1,37 @@
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#include <climits>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
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);
|
||||
extern "C" int OpusPdfSnapshotAddRun(const char*, int, const char*, int,
|
||||
int, int, int, int, int, int, int, int);
|
||||
|
||||
std::string read_file(const std::string& path) {
|
||||
std::ifstream input(path, std::ios::binary);
|
||||
return {std::istreambuf_iterator<char>(input), {}};
|
||||
}
|
||||
|
||||
int main(const int argument_count, char** arguments) {
|
||||
if (argument_count == 4 && std::strcmp(arguments[1], "--text") == 0) {
|
||||
const bool converted =
|
||||
OpusModernDocxToTextFile(arguments[2], arguments[3]) != 0;
|
||||
std::cout << "DOCX text import "
|
||||
<< (converted ? "passed" : "failed") << '\n';
|
||||
return converted ? 0 : 4;
|
||||
}
|
||||
if (argument_count == 3) {
|
||||
const bool converted =
|
||||
OpusModernDocxToRtfFile(arguments[1], arguments[2]) != 0;
|
||||
@@ -32,6 +54,9 @@ 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]{};
|
||||
const DWORD requested_pdf_length = GetEnvironmentVariableA(
|
||||
"WORD1_TEST_KEEP_PDF", requested_pdf,
|
||||
@@ -45,12 +70,63 @@ 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);
|
||||
output << "ORIGINAL";
|
||||
}
|
||||
HANDLE oversized_file = CreateFileA(
|
||||
oversized.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS,
|
||||
FILE_ATTRIBUTE_TEMPORARY, nullptr);
|
||||
bool oversized_created = oversized_file != INVALID_HANDLE_VALUE;
|
||||
if (oversized_created) {
|
||||
LARGE_INTEGER hostile_size{};
|
||||
hostile_size.QuadPart = 64ll * 1024ll * 1024ll + 1;
|
||||
oversized_created = SetFilePointerEx(oversized_file, hostile_size,
|
||||
nullptr, FILE_BEGIN) &&
|
||||
SetEndOfFile(oversized_file);
|
||||
CloseHandle(oversized_file);
|
||||
}
|
||||
const bool oversized_rejected = oversized_created &&
|
||||
OpusModernRtfFileToPdf(oversized.c_str(), preserved.c_str()) == 0 &&
|
||||
read_file(preserved) == "ORIGINAL";
|
||||
const bool invalid_snapshot_rejected =
|
||||
OpusPdfSnapshotBegin(INT_MAX, INT_MAX, INT_MAX, INT_MAX,
|
||||
INT_MAX, INT_MAX) == 0 &&
|
||||
OpusPdfSnapshotBegin(12240, 15840, 1440, 1440, 1440, 1440) != 0 &&
|
||||
OpusPdfSnapshotAddParagraph(0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0) != 0 &&
|
||||
OpusPdfSnapshotAddParagraph(0, 0, 0, 0, 0, 0, -240,
|
||||
0, 0, 0, 0) != 0 &&
|
||||
OpusPdfSnapshotAddRun("x", INT_MAX, "Arial", 20,
|
||||
0, 0, 0, 0, 0, 0, 0, 0) == 0;
|
||||
std::string result;
|
||||
if (read) {
|
||||
std::ifstream input(roundtrip, std::ios::binary);
|
||||
@@ -64,21 +140,36 @@ 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) {
|
||||
pdf_data.find("%%EOF") == std::string::npos ||
|
||||
!oversized_rejected || !invalid_snapshot_rejected) {
|
||||
std::cerr << "DOCX round trip failed: write=" << written
|
||||
<< " read=" << read << " pdf=" << pdf_written
|
||||
<< " bytes=" << result.size() << '\n';
|
||||
<< " bytes=" << result.size()
|
||||
<< " oversizedRejected=" << oversized_rejected
|
||||
<< " invalidSnapshotRejected="
|
||||
<< invalid_snapshot_rejected << '\n';
|
||||
return 2;
|
||||
}
|
||||
std::cout << "DOCX round trip passed (" << result.size() << " RTF bytes)\n";
|
||||
|
||||
@@ -374,28 +374,6 @@ LONG WINAPI ObserveVectoredException(EXCEPTION_POINTERS* exception) {
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" void OpusX64TraceRibbon(const char* stage, int message, int tmc,
|
||||
int first_value, int second_value,
|
||||
long cp_first, long cp_limit,
|
||||
int insertion) {
|
||||
char trace_path[MAX_PATH] = {};
|
||||
BuildDiagnosticPath("WORD1-ribbon.txt", trace_path, sizeof(trace_path));
|
||||
HANDLE file = CreateFileA(trace_path, FILE_APPEND_DATA,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
|
||||
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
if (file == INVALID_HANDLE_VALUE) {
|
||||
return;
|
||||
}
|
||||
char line[384] = {};
|
||||
std::snprintf(line, sizeof(line),
|
||||
"%llu %s msg=%d tmc=%d a=%d b=%d sel=%ld,%ld ins=%d\r\n",
|
||||
static_cast<unsigned long long>(GetTickCount64()),
|
||||
stage != nullptr ? stage : "", message, tmc, first_value,
|
||||
second_value, cp_first, cp_limit, insertion);
|
||||
WriteCrashText(file, line);
|
||||
CloseHandle(file);
|
||||
}
|
||||
|
||||
int WINAPI wWinMain(HINSTANCE instance, HINSTANCE previous,
|
||||
PWSTR command_line, int show_command) {
|
||||
if ((command_line != nullptr &&
|
||||
@@ -404,6 +382,15 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE previous,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Exclude the current directory and PATH from DLL resolution. The app
|
||||
directory remains available for intentionally deployed components and
|
||||
system DLLs are resolved only from System32. */
|
||||
SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_APPLICATION_DIR |
|
||||
LOAD_LIBRARY_SEARCH_SYSTEM32 |
|
||||
LOAD_LIBRARY_SEARCH_USER_DIRS);
|
||||
SetSearchPathMode(BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE |
|
||||
BASE_SEARCH_PATH_PERMANENT);
|
||||
|
||||
SetUnhandledExceptionFilter(WriteCrashStack);
|
||||
AddVectoredExceptionHandler(1, ObserveVectoredException);
|
||||
_RTC_SetErrorFuncW(WriteRtcFailure);
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
#include "opus_x64_compat.h"
|
||||
#include "opus_x64_heap.h"
|
||||
#include <commdlg.h>
|
||||
#include <objbase.h>
|
||||
extern "C" {
|
||||
#include "dac.h"
|
||||
}
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
@@ -21,10 +23,17 @@ extern std::uintptr_t wRefDlgCur;
|
||||
extern HWND vhWndMsgBoxParent;
|
||||
void GetCabSz(void**, char*, std::uint16_t, std::uint16_t);
|
||||
int FSetCabSz(void**, const char*, std::uint16_t);
|
||||
void OpusX64TraceRibbon(const char*, int, int, int, int, long, long, int);
|
||||
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,
|
||||
long, long, int) {
|
||||
/* Ribbon tracing used during the bring-up of the x64 port. Keep the ABI
|
||||
for original call sites without writing diagnostic files in product or
|
||||
test processes. */
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -131,6 +140,7 @@ struct Win95SaveAlias {
|
||||
|
||||
Win95SaveAlias g_win95_save_alias;
|
||||
std::unordered_map<std::string, std::string> g_win95_saved_aliases;
|
||||
std::string g_win95_staging_directory;
|
||||
|
||||
struct Win95AliasCleanup {
|
||||
~Win95AliasCleanup() {
|
||||
@@ -141,6 +151,9 @@ struct Win95AliasCleanup {
|
||||
for (const auto& saved : g_win95_saved_aliases) {
|
||||
DeleteFileA(saved.first.c_str());
|
||||
}
|
||||
if (!g_win95_staging_directory.empty()) {
|
||||
RemoveDirectoryA(g_win95_staging_directory.c_str());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -154,43 +167,131 @@ std::string win95_alias_key(std::string path) {
|
||||
return path;
|
||||
}
|
||||
|
||||
bool safe_dialog_file_path(const std::string& path, const bool must_exist) {
|
||||
if (path.empty() || path.size() >= 32760 ||
|
||||
path.starts_with(R"(\\.\)") ||
|
||||
path.starts_with(R"(\\?\GLOBALROOT\)")) return false;
|
||||
for (std::size_t index = 0; index < path.size(); ++index) {
|
||||
if (path[index] == ':' && index != 1) return false;
|
||||
}
|
||||
const DWORD attributes = GetFileAttributesA(path.c_str());
|
||||
if (attributes != INVALID_FILE_ATTRIBUTES)
|
||||
return (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
|
||||
return !must_exist && (GetLastError() == ERROR_FILE_NOT_FOUND ||
|
||||
GetLastError() == ERROR_PATH_NOT_FOUND);
|
||||
}
|
||||
|
||||
bool import_file_within_limit(const std::string& path) {
|
||||
if (!safe_dialog_file_path(path, true)) return false;
|
||||
WIN32_FILE_ATTRIBUTE_DATA attributes{};
|
||||
if (!GetFileAttributesExA(path.c_str(), GetFileExInfoStandard,
|
||||
&attributes)) return false;
|
||||
constexpr ULONGLONG kMaximumImportBytes = 256ull * 1024ull * 1024ull;
|
||||
const ULONGLONG size =
|
||||
(static_cast<ULONGLONG>(attributes.nFileSizeHigh) << 32) |
|
||||
attributes.nFileSizeLow;
|
||||
return size <= kMaximumImportBytes;
|
||||
}
|
||||
|
||||
bool make_win95_staging_path(std::string& path,
|
||||
const char* desired_extension = ".DOC") {
|
||||
char module_path[32768]{};
|
||||
const DWORD module_length = GetModuleFileNameA(
|
||||
nullptr, module_path, static_cast<DWORD>(std::size(module_path)));
|
||||
if (module_length == 0 || module_length >= std::size(module_path)) {
|
||||
return false;
|
||||
}
|
||||
char* separator = std::strrchr(module_path, '\\');
|
||||
if (separator == nullptr) {
|
||||
return false;
|
||||
}
|
||||
*separator = '\0';
|
||||
std::string directory = std::string(module_path) + "\\W95TEMP";
|
||||
if (!CreateDirectoryA(directory.c_str(), nullptr) &&
|
||||
GetLastError() != ERROR_ALREADY_EXISTS) {
|
||||
return false;
|
||||
}
|
||||
for (int attempt = 0; attempt < 32; ++attempt) {
|
||||
char temporary[MAX_PATH]{};
|
||||
if (GetTempFileNameA(directory.c_str(), "W95", 0, temporary) == 0) {
|
||||
if (_stricmp(desired_extension, ".DOC") != 0 &&
|
||||
_stricmp(desired_extension, ".TXT") != 0) return false;
|
||||
if (g_win95_staging_directory.empty()) {
|
||||
char temporary_root[32768]{};
|
||||
const DWORD root_length = GetTempPathA(
|
||||
static_cast<DWORD>(std::size(temporary_root)), temporary_root);
|
||||
if (root_length == 0 || root_length >= std::size(temporary_root))
|
||||
return false;
|
||||
for (int attempt = 0; attempt < 32; ++attempt) {
|
||||
GUID identifier{};
|
||||
if (FAILED(CoCreateGuid(&identifier))) return false;
|
||||
char leaf[9]{};
|
||||
_snprintf_s(leaf, std::size(leaf), _TRUNCATE, "W%07lX",
|
||||
identifier.Data1 & 0x0ffffffful);
|
||||
const std::string candidate = std::string(temporary_root) + leaf;
|
||||
/* The original normalizer requires DOS 8.3-safe path components
|
||||
even though the native file APIs support long names. */
|
||||
if (candidate.size() >= 52) return false;
|
||||
if (CreateDirectoryA(candidate.c_str(), nullptr)) {
|
||||
const DWORD attributes = GetFileAttributesA(candidate.c_str());
|
||||
if (attributes == INVALID_FILE_ATTRIBUTES ||
|
||||
(attributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
|
||||
RemoveDirectoryA(candidate.c_str());
|
||||
return false;
|
||||
}
|
||||
g_win95_staging_directory = candidate;
|
||||
break;
|
||||
}
|
||||
if (GetLastError() != ERROR_ALREADY_EXISTS) return false;
|
||||
}
|
||||
DeleteFileA(temporary);
|
||||
char* existing_extension = std::strrchr(temporary, '.');
|
||||
if (existing_extension != nullptr) {
|
||||
lstrcpyA(existing_extension, desired_extension);
|
||||
}
|
||||
if (GetFileAttributesA(temporary) == INVALID_FILE_ATTRIBUTES &&
|
||||
std::strlen(temporary) < 120) {
|
||||
path = temporary;
|
||||
if (g_win95_staging_directory.empty()) return false;
|
||||
}
|
||||
static std::atomic<unsigned long> sequence{0};
|
||||
for (int attempt = 0; attempt < 32; ++attempt) {
|
||||
char leaf[18]{};
|
||||
_snprintf_s(leaf, std::size(leaf), _TRUNCATE, "\\D%07lX%s",
|
||||
++sequence & 0x0ffffffful, desired_extension);
|
||||
const std::string candidate = g_win95_staging_directory + leaf;
|
||||
if (candidate.size() >= 120) return false;
|
||||
const DWORD attributes = GetFileAttributesA(candidate.c_str());
|
||||
const DWORD attribute_error = GetLastError();
|
||||
if (attributes == INVALID_FILE_ATTRIBUTES &&
|
||||
(attribute_error == ERROR_FILE_NOT_FOUND ||
|
||||
attribute_error == ERROR_PATH_NOT_FOUND)) {
|
||||
path = candidate;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool atomic_copy_file(const std::string& source, const std::string& target) {
|
||||
if (source.empty() || target.empty()) return false;
|
||||
static std::atomic<unsigned long> sequence{0};
|
||||
std::string temporary;
|
||||
bool copied = false;
|
||||
for (int attempt = 0; attempt < 64; ++attempt) {
|
||||
temporary = target + ".word1tmp-" +
|
||||
std::to_string(GetCurrentProcessId()) + "-" +
|
||||
std::to_string(++sequence);
|
||||
if (CopyFileA(source.c_str(), temporary.c_str(), TRUE)) {
|
||||
copied = true;
|
||||
break;
|
||||
}
|
||||
if (GetLastError() != ERROR_FILE_EXISTS &&
|
||||
GetLastError() != ERROR_ALREADY_EXISTS) return false;
|
||||
}
|
||||
if (!copied) return false;
|
||||
HANDLE file = CreateFileA(temporary.c_str(), GENERIC_WRITE,
|
||||
FILE_SHARE_READ, nullptr, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
const bool flushed = file != INVALID_HANDLE_VALUE &&
|
||||
FlushFileBuffers(file) != FALSE;
|
||||
if (file != INVALID_HANDLE_VALUE) CloseHandle(file);
|
||||
if (!flushed) {
|
||||
DeleteFileA(temporary.c_str());
|
||||
return false;
|
||||
}
|
||||
const DWORD attributes = GetFileAttributesA(target.c_str());
|
||||
bool committed = false;
|
||||
if (attributes != INVALID_FILE_ATTRIBUTES &&
|
||||
(attributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
|
||||
committed = ReplaceFileA(target.c_str(), temporary.c_str(), nullptr,
|
||||
REPLACEFILE_WRITE_THROUGH, nullptr, nullptr) != FALSE;
|
||||
if (!committed) {
|
||||
committed = MoveFileExA(temporary.c_str(), target.c_str(),
|
||||
MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH) != FALSE;
|
||||
}
|
||||
} else if (attributes == INVALID_FILE_ATTRIBUTES &&
|
||||
GetLastError() == ERROR_FILE_NOT_FOUND) {
|
||||
committed = MoveFileExA(temporary.c_str(), target.c_str(),
|
||||
MOVEFILE_WRITE_THROUGH) != FALSE;
|
||||
}
|
||||
if (!committed) DeleteFileA(temporary.c_str());
|
||||
return committed;
|
||||
}
|
||||
|
||||
std::string counted_path(const unsigned char* st_file) {
|
||||
if (st_file == nullptr || st_file[0] == 0 || st_file[0] >= 120) {
|
||||
return {};
|
||||
@@ -2187,9 +2288,13 @@ Tmc run_word95_common_file_dialog(DialogState& dialog) {
|
||||
}
|
||||
|
||||
char test_path[32768]{};
|
||||
#ifdef OPUS_TEST_HOOKS
|
||||
const DWORD test_path_length = GetEnvironmentVariableA(
|
||||
"WORD1_TEST_FILE_DIALOG_PATH", test_path,
|
||||
static_cast<DWORD>(std::size(test_path)));
|
||||
#else
|
||||
const DWORD test_path_length = 0;
|
||||
#endif
|
||||
BOOL accepted = FALSE;
|
||||
if (test_path_length > 0 && test_path_length < std::size(test_path)) {
|
||||
lstrcpynA(file_buffer.data(), test_path,
|
||||
@@ -2197,7 +2302,9 @@ Tmc run_word95_common_file_dialog(DialogState& dialog) {
|
||||
if (saving && OpusModernPathIsDocx(test_path)) {
|
||||
file_dialog.nFilterIndex = 2;
|
||||
}
|
||||
#ifdef OPUS_TEST_HOOKS
|
||||
SetEnvironmentVariableA("WORD1_TEST_FILE_DIALOG_PATH", nullptr);
|
||||
#endif
|
||||
accepted = TRUE;
|
||||
} else {
|
||||
accepted = opening ? GetOpenFileNameA(&file_dialog) :
|
||||
@@ -2232,6 +2339,16 @@ Tmc run_word95_common_file_dialog(DialogState& dialog) {
|
||||
".docx");
|
||||
}
|
||||
}
|
||||
if ((opening && !import_file_within_limit(selected_path)) ||
|
||||
(saving && !safe_dialog_file_path(selected_path, false))) {
|
||||
MessageBoxA(owner,
|
||||
opening ?
|
||||
"This document is not a regular file or is too large to open safely." :
|
||||
"This is not a safe document file name.",
|
||||
opening ? "Open" : "Save As",
|
||||
MB_OK | MB_ICONEXCLAMATION);
|
||||
continue;
|
||||
}
|
||||
std::string legacy_path;
|
||||
const bool docx = OpusModernPathIsDocx(selected_path.c_str()) != 0;
|
||||
if (!make_win95_staging_path(legacy_path,
|
||||
@@ -2411,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;
|
||||
@@ -2425,11 +2542,10 @@ 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 :
|
||||
CopyFileA(g_win95_save_alias.legacy_path.c_str(),
|
||||
g_win95_save_alias.selected_path.c_str(), FALSE) != 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);
|
||||
}
|
||||
if (!success) {
|
||||
DeleteFileA(g_win95_save_alias.legacy_path.c_str());
|
||||
@@ -2449,8 +2565,8 @@ int OpusFinishWin95SaveAlias(const unsigned char* st_file,
|
||||
return false;
|
||||
}
|
||||
return OpusModernPathIsDocx(saved->second.c_str()) ?
|
||||
OpusModernRtfFileToDocx(path.c_str(), saved->second.c_str()) != 0 :
|
||||
CopyFileA(path.c_str(), saved->second.c_str(), FALSE) != 0;
|
||||
OpusSaveDocumentAsDocx(doc, saved->second.c_str()) != 0 :
|
||||
atomic_copy_file(path, saved->second);
|
||||
}
|
||||
|
||||
int OpusWin95SaveAliasRequiresRtf(const unsigned char* st_file) {
|
||||
|
||||
@@ -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;
|
||||
@@ -227,7 +279,8 @@ int scale(HWND window, int value) {
|
||||
|
||||
void set_window_classic(HWND window) {
|
||||
using SetWindowThemeProc = HRESULT(WINAPI*)(HWND, LPCWSTR, LPCWSTR);
|
||||
static HMODULE theme_module = LoadLibraryW(L"uxtheme.dll");
|
||||
static HMODULE theme_module = LoadLibraryExW(
|
||||
L"uxtheme.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
||||
static const auto set_theme = theme_module != nullptr ?
|
||||
reinterpret_cast<SetWindowThemeProc>(
|
||||
GetProcAddress(theme_module, "SetWindowTheme")) : nullptr;
|
||||
@@ -346,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();
|
||||
@@ -454,7 +534,8 @@ void configure_word95_menus(HWND window) {
|
||||
void apply_caption_colors(HWND window) {
|
||||
using DwmSetWindowAttributeProc = HRESULT(WINAPI*)(HWND, DWORD,
|
||||
LPCVOID, DWORD);
|
||||
HMODULE module = LoadLibraryW(L"dwmapi.dll");
|
||||
HMODULE module = LoadLibraryExW(
|
||||
L"dwmapi.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
||||
if (module == nullptr) {
|
||||
return;
|
||||
}
|
||||
@@ -2311,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 = {};
|
||||
@@ -2507,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) {
|
||||
@@ -2588,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;
|
||||
@@ -2597,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) {
|
||||
@@ -2864,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;
|
||||
@@ -959,8 +962,10 @@ int wmain(const int argument_count, wchar_t** arguments) {
|
||||
}
|
||||
}
|
||||
bool rich_pdf_valid = !docx_pdf_export_mode;
|
||||
if (docx_pdf_export_mode &&
|
||||
PostMessageW(main_window, kWmCommand, kExportPdf, 0)) {
|
||||
DWORD_PTR export_result = 0;
|
||||
if (docx_pdf_export_mode && SendMessageTimeoutW(
|
||||
main_window, kWmCommand, kExportPdf, 0,
|
||||
SMTO_ABORTIFHUNG | SMTO_BLOCK, 30000, &export_result)) {
|
||||
const ULONGLONG pdf_deadline = GetTickCount64() + 15000;
|
||||
do {
|
||||
std::ifstream input(pdf_path, std::ios::binary);
|
||||
@@ -1001,7 +1006,11 @@ int wmain(const int argument_count, wchar_t** arguments) {
|
||||
<< "} bodyFont={index:" << body_font_index
|
||||
<< ",charset:" << body_font_charset << "}\n";
|
||||
if (docx_pdf_export_mode)
|
||||
std::cerr << "richPdf=" << rich_pdf_valid << '\n';
|
||||
std::cerr << "richPdf=" << rich_pdf_valid
|
||||
<< " exportStage="
|
||||
<< (pane != nullptr ? SendMessageW(
|
||||
pane, kWmOpusX64QuerySelection, 105, 0) : -1)
|
||||
<< '\n';
|
||||
if (!keep_pdf && !pdf_path.empty()) DeleteFileA(pdf_path.c_str());
|
||||
return 84;
|
||||
}
|
||||
@@ -1091,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;
|
||||
|
||||
@@ -29,14 +29,18 @@ static_assert(offsetof(NativeHandle, data) == 0);
|
||||
constexpr int kPrcTokenNil = 0x3fff;
|
||||
constexpr int kPrcTokenMac = kPrcTokenNil - 1;
|
||||
constexpr int kCompactHandleMac = 0x3fff;
|
||||
constexpr std::size_t kNativeHandleSlots = 131071;
|
||||
constexpr std::size_t kSegmentSlots = 65536;
|
||||
constexpr std::size_t kGuardBytes = 64;
|
||||
constexpr std::size_t kMaxAllocationBytes = 256u * 1024u * 1024u;
|
||||
constexpr unsigned char kGuardValue = 0xa5;
|
||||
SRWLOCK prc_registry_lock = SRWLOCK_INIT;
|
||||
SRWLOCK compact_handle_lock = SRWLOCK_INIT;
|
||||
SRWLOCK native_handle_lock = SRWLOCK_INIT;
|
||||
SRWLOCK segment_registry_lock = SRWLOCK_INIT;
|
||||
void** prc_handles[kPrcTokenNil]{};
|
||||
void** compact_handles[kCompactHandleMac + 1]{};
|
||||
void** native_handles[kNativeHandleSlots]{};
|
||||
std::array<NativeSegment, kSegmentSlots> native_segments{};
|
||||
std::atomic_size_t heap_bytes_used{0};
|
||||
|
||||
@@ -47,6 +51,7 @@ std::atomic_size_t heap_bytes_used{0};
|
||||
|
||||
[[nodiscard]] constexpr bool guarded_size(
|
||||
const std::size_t logical_size, std::size_t* result) noexcept {
|
||||
if (logical_size > kMaxAllocationBytes) return false;
|
||||
const std::size_t payload = payload_size(logical_size);
|
||||
if (payload > (std::numeric_limits<std::size_t>::max)() - kGuardBytes) {
|
||||
return false;
|
||||
@@ -55,6 +60,75 @@ std::atomic_size_t heap_bytes_used{0};
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool valid_native_handle(void** opaque_handle) noexcept {
|
||||
if (opaque_handle == nullptr) return false;
|
||||
void** const tombstone = reinterpret_cast<void**>(static_cast<uintptr_t>(1));
|
||||
const std::size_t start =
|
||||
(reinterpret_cast<uintptr_t>(opaque_handle) >> 4) % kNativeHandleSlots;
|
||||
AcquireSRWLockShared(&native_handle_lock);
|
||||
bool valid = false;
|
||||
for (std::size_t probe = 0; probe < kNativeHandleSlots; ++probe) {
|
||||
void** const candidate =
|
||||
native_handles[(start + probe) % kNativeHandleSlots];
|
||||
if (candidate == opaque_handle) {
|
||||
valid = true;
|
||||
break;
|
||||
}
|
||||
if (candidate == nullptr) break;
|
||||
if (candidate == tombstone) continue;
|
||||
}
|
||||
ReleaseSRWLockShared(&native_handle_lock);
|
||||
return valid;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool register_native_handle(void** opaque_handle) noexcept {
|
||||
void** const tombstone = reinterpret_cast<void**>(static_cast<uintptr_t>(1));
|
||||
const std::size_t start =
|
||||
(reinterpret_cast<uintptr_t>(opaque_handle) >> 4) % kNativeHandleSlots;
|
||||
AcquireSRWLockExclusive(&native_handle_lock);
|
||||
std::size_t first_tombstone = kNativeHandleSlots;
|
||||
for (std::size_t probe = 0; probe < kNativeHandleSlots; ++probe) {
|
||||
const std::size_t index = (start + probe) % kNativeHandleSlots;
|
||||
if (native_handles[index] == opaque_handle) {
|
||||
ReleaseSRWLockExclusive(&native_handle_lock);
|
||||
return true;
|
||||
}
|
||||
if (native_handles[index] == tombstone &&
|
||||
first_tombstone == kNativeHandleSlots) first_tombstone = index;
|
||||
if (native_handles[index] == nullptr) {
|
||||
native_handles[first_tombstone == kNativeHandleSlots ?
|
||||
index : first_tombstone] = opaque_handle;
|
||||
ReleaseSRWLockExclusive(&native_handle_lock);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (first_tombstone != kNativeHandleSlots) {
|
||||
native_handles[first_tombstone] = opaque_handle;
|
||||
ReleaseSRWLockExclusive(&native_handle_lock);
|
||||
return true;
|
||||
}
|
||||
ReleaseSRWLockExclusive(&native_handle_lock);
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool unregister_native_handle(void** opaque_handle) noexcept {
|
||||
void** const tombstone = reinterpret_cast<void**>(static_cast<uintptr_t>(1));
|
||||
const std::size_t start =
|
||||
(reinterpret_cast<uintptr_t>(opaque_handle) >> 4) % kNativeHandleSlots;
|
||||
AcquireSRWLockExclusive(&native_handle_lock);
|
||||
for (std::size_t probe = 0; probe < kNativeHandleSlots; ++probe) {
|
||||
const std::size_t index = (start + probe) % kNativeHandleSlots;
|
||||
if (native_handles[index] == opaque_handle) {
|
||||
native_handles[index] = tombstone;
|
||||
ReleaseSRWLockExclusive(&native_handle_lock);
|
||||
return true;
|
||||
}
|
||||
if (native_handles[index] == nullptr) break;
|
||||
}
|
||||
ReleaseSRWLockExclusive(&native_handle_lock);
|
||||
return false;
|
||||
}
|
||||
|
||||
void set_guard(void* const allocation,
|
||||
const std::size_t logical_size) noexcept {
|
||||
std::memset(static_cast<unsigned char*>(allocation) +
|
||||
@@ -126,9 +200,11 @@ extern "C" HP OpusHpOfSbIbImpl(const SB segment, const uintptr_t offset) {
|
||||
return reinterpret_cast<HP>(static_cast<uintptr_t>(segment) + offset);
|
||||
}
|
||||
AcquireSRWLockShared(&segment_registry_lock);
|
||||
void* base = native_segments[static_cast<std::size_t>(segment)].data;
|
||||
const auto& slot = native_segments[static_cast<std::size_t>(segment)];
|
||||
void* base = slot.data;
|
||||
const std::size_t size = slot.size;
|
||||
ReleaseSRWLockShared(&segment_registry_lock);
|
||||
return base == nullptr
|
||||
return base == nullptr || offset > size
|
||||
? nullptr
|
||||
: reinterpret_cast<HP>(reinterpret_cast<uintptr_t>(base) +
|
||||
offset);
|
||||
@@ -323,7 +399,7 @@ extern "C" int FAssureHcb(void*** handle_address,
|
||||
const int byte_count_needed, int* byte_count,
|
||||
int* byte_capacity) {
|
||||
if (handle_address == nullptr || byte_count_needed < 0 ||
|
||||
byte_capacity == nullptr) {
|
||||
byte_capacity == nullptr || *byte_capacity < 0) {
|
||||
return 0;
|
||||
}
|
||||
if (byte_count != nullptr) {
|
||||
@@ -332,7 +408,10 @@ extern "C" int FAssureHcb(void*** handle_address,
|
||||
if (*handle_address != nullptr && byte_count_needed <= *byte_capacity) {
|
||||
return 1;
|
||||
}
|
||||
const int doubled = *byte_capacity > 0 ? *byte_capacity * 2 : 16;
|
||||
const int doubled = *byte_capacity >
|
||||
(std::numeric_limits<int>::max)() / 2 ?
|
||||
(std::numeric_limits<int>::max)() :
|
||||
(*byte_capacity > 0 ? *byte_capacity * 2 : 16);
|
||||
const int new_capacity = (std::max)(byte_count_needed, doubled);
|
||||
if (*handle_address == nullptr) {
|
||||
*handle_address = OpusHAllocateCb(static_cast<std::size_t>(new_capacity));
|
||||
@@ -397,6 +476,11 @@ extern "C" void** OpusHAllocateCb(const std::size_t byte_count) {
|
||||
}
|
||||
set_guard(handle->data, byte_count);
|
||||
handle->size = byte_count;
|
||||
if (!register_native_handle(reinterpret_cast<void**>(handle))) {
|
||||
HeapFree(process_heap(), 0, handle->data);
|
||||
HeapFree(process_heap(), 0, handle);
|
||||
return nullptr;
|
||||
}
|
||||
heap_bytes_used.fetch_add(byte_count, std::memory_order_relaxed);
|
||||
return reinterpret_cast<void**>(handle);
|
||||
}
|
||||
@@ -405,6 +489,9 @@ extern "C" void OpusFreeH(void** opaque_handle) {
|
||||
if (opaque_handle == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (!unregister_native_handle(opaque_handle)) {
|
||||
report_guard_failure();
|
||||
}
|
||||
unregister_prc_handle(opaque_handle);
|
||||
unregister_compact_handle(opaque_handle);
|
||||
auto* handle = reinterpret_cast<NativeHandle*>(opaque_handle);
|
||||
@@ -433,6 +520,9 @@ extern "C" int OpusFChngSizeHCb(void** opaque_handle,
|
||||
if (opaque_handle == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
if (!valid_native_handle(opaque_handle)) {
|
||||
report_guard_failure();
|
||||
}
|
||||
auto* handle = reinterpret_cast<NativeHandle*>(opaque_handle);
|
||||
if (!allow_shrink && byte_count <= handle->size) {
|
||||
return 1;
|
||||
@@ -470,7 +560,7 @@ extern "C" int OpusFChngSizePhqLcb(void*** handle_address,
|
||||
}
|
||||
|
||||
extern "C" size_t OpusCbOfH(void** opaque_handle) {
|
||||
return opaque_handle == nullptr
|
||||
return !valid_native_handle(opaque_handle)
|
||||
? 0
|
||||
: reinterpret_cast<NativeHandle*>(opaque_handle)->size;
|
||||
}
|
||||
@@ -480,12 +570,13 @@ extern "C" size_t OpusHeapBytesUsed() {
|
||||
}
|
||||
|
||||
extern "C" void* OpusDerefH(void** opaque_handle) {
|
||||
return opaque_handle == nullptr
|
||||
return !valid_native_handle(opaque_handle)
|
||||
? nullptr
|
||||
: reinterpret_cast<NativeHandle*>(opaque_handle)->data;
|
||||
}
|
||||
|
||||
extern "C" void* OpusHpAlloc(const std::size_t byte_count) {
|
||||
if (byte_count > kMaxAllocationBytes) return nullptr;
|
||||
void* allocation =
|
||||
HeapAlloc(process_heap(), 0, byte_count == 0 ? 1 : byte_count);
|
||||
if (allocation != nullptr) {
|
||||
@@ -500,9 +591,8 @@ extern "C" void* OpusHpAlloc(const std::size_t byte_count) {
|
||||
extern "C" void OpusFreeHp(void* pointer) {
|
||||
if (pointer != nullptr) {
|
||||
const SIZE_T size = HeapSize(process_heap(), 0, pointer);
|
||||
if (size != static_cast<SIZE_T>(-1)) {
|
||||
heap_bytes_used.fetch_sub(size, std::memory_order_relaxed);
|
||||
}
|
||||
if (size == static_cast<SIZE_T>(-1)) report_guard_failure();
|
||||
heap_bytes_used.fetch_sub(size, std::memory_order_relaxed);
|
||||
HeapFree(process_heap(), 0, pointer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,7 +325,8 @@ int main() {
|
||||
EndSdm();
|
||||
return 23;
|
||||
}
|
||||
TestDltHeader modal_template{{8, 24, 206, 104}, 3, 0x8400,
|
||||
/* Character is native-modal but is not routed to a common file dialog. */
|
||||
TestDltHeader modal_template{{8, 24, 206, 104}, 16, 0x8400,
|
||||
reinterpret_cast<void*>(ModalRuntimeProbe),
|
||||
11, 4};
|
||||
auto* modal_template_pointer = &modal_template;
|
||||
|
||||
Reference in New Issue
Block a user