Another approach. This will shield arguments to Hold from replacement attempts, on any level:
line1Points = {{1, 4}, {10, 4}};
line2Points = {{1, 5}, {10, 5}}
xRange = {-1, 11};
yRange = {-1, 7};
arrowPoints = {{7, 4.5}, {7 - Cos[Pi/4], 3.5 + Sin[Pi/4]}, {6, 3.5}};
Graphics[{RGBColor["RoyalBlue"],
Line[{line1Points, line2Points}],
Red, Arrowheads[0.05],
Arrow[BezierCurve[arrowPoints]]},
PlotRange -> {xRange, yRange},
AspectRatio -> Automatic]
replaceAllNotHeld[expr_, reprules_] := Module[
{allPos, heldPos, repPos},
allPos = Position[expr, _];
heldPos = Position[expr, Hold] /. 0 -> __;
repPos = Intersection @@ (DeleteCases[allPos, #] & /@ heldPos);
MapAt[Replace[#, reprules] &, expr, repPos]
]
Example:
In[2]:= testExpr1 = {Hold@"A", "B", {Hold@"C", "D", Text[Hold@"E", "F"]}}
Out[2]= {Hold["A"], "B", {Hold["C"], "D", Text[Hold["E"], "F"]}}
In[3]:= replaceAllNotHeld[testExpr1, x_String :> LetterNumber@x] // ReleaseHold
Out[3]= {"A", 2, {"C", 4, Text["E", 6]}}